Brief Description

In this notebook, we document Capybara analysis of the Paul et al., 2015 hematopoiesis dataset, charting differentiation of bone marrow-derived myeloid progenitors. We use this dataset to showcase the application of Capybara to a well-defined developmental process. We leverage PAGA-based pseudotime information to compare hybrid cells to their discrete counterparts, demonstrating the biological relevance of hybrid cells.

For details of the dataset, please refer to the Paul paper here (https://www.sciencedirect.com/science/article/pii/S0092867415014932). For details of Capybara cell-type classification, please refer to the Capybara paper here (https://www.sciencedirect.com/science/article/pii/S1934590922000996?dgcid=coauthor).

Load packages

library(Seurat)
library(ggplot2)
library(SeuratDisk)
library(ggrepel)

Basic Information

Basic information is obtained from the following tutorial: https://scanpy-tutorials.readthedocs.io/en/latest/paga-paul15.html. Please find more information in the Jupyter notebook supplied there.

Load the count matrix

Here we load the Paul et al 2015 count matrix, sourced from the PAGA tutorial.

paul_csv <- read.csv("~/Desktop/Reproducibility/Figure 2/Intermediates/Data/paul_count_matx.csv", check.names = F, stringsAsFactors = F, row.names = 1)
paul_csv_t <- as.data.frame(t(paul_csv))
colnames(paul_csv_t) <- paste0("Cell_", colnames(paul_csv_t))

Load the coordinates

Here we load the coordinates for the force atlas embedding, guided by PAGA, for downstream visualization.

coord <- read.csv("~/Desktop/Reproducibility/Figure 2/Intermediates/Data/coordinates.csv", header = F)
rownames(coord) <- paste0("Cell_", (seq(nrow(coord)) - 1))

Load meta data

Here we load the coordinates for the force atlas embedding, guided by PAGA, for downstream visualization.

meta <- read.csv("~/Desktop/Reproducibility/Figure 2/Intermediates/Data/meta_data.csv", header = T, row.names = 1, check.names = F, stringsAsFactors = F)
rownames(meta) <- paste0("Cell_", rownames(meta))

We merge the meta data with the coordinates

meta <- cbind(meta, coord[rownames(meta),])

Pseudotime Projection

Here we have a quick check on the pseudotime projection with the proper coordinates to make sure all cells are properly located on the FA plot.

ggplot(meta, aes(x = V1, y = V2, color = dpt_pseudotime)) +
  geom_point() +
  scale_color_viridis_c(name = "pseudotime", direction = 1, option = "A") +
  theme(legend.position="right",
        axis.text.x = element_blank(),
        axis.text.y = element_blank(),
        axis.title.x = element_blank(),
        axis.title.y = element_blank(),
        panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank(),
        panel.background = element_blank(), 
        title = element_text(face = "bold.italic", size = 14),
        axis.line = element_blank(),
        axis.ticks = element_blank())

Capybara

Briefly, we apply Capybara on the dataset loaded above. We do NOT include the MCA data or the bulk selection step here considering its relatively large size. The bulk selection step provided 4 relevant tissues: bone marrow, bone marrow ckit (grouped as ‘bone marrow’ in the manuscript), bone marrow mesenchyme (primary mesenchymal stem cells), and peripheral blood. We do include the pipeline for construction of the high resolution reference. If you would like to run this from the raw MCA data, please download the data here (http://bis.zju.edu.cn/MCA/atlas2.html), edit the directories and reconstruct the high-resolution reference.

In general, for runtime consideration, we processed the dataset on a High Performance Computing resource. Hence, we include the intermediate files, such as the reference, QP outcomes, and permutation results, in this folder for faster processing.

Load packages

library(Capybara)
library(MASS)

Construct the high resolution reference from the Mouse Cell Atlas

  1. Here we will load the MCA data related to the 4 relevant tissues.
# Background cells
mca <- read.csv("~/Box Sync/Morris Lab/Classifier Analysis/Reference datasets/MCA/MCA_CellAssignments.csv",
                row.names = 1, header = T, stringsAsFactors = F)
mca.meta <- data.frame(row.names = mca$Cell.name, 
                       tissue = mca$Tissue,
                       cell.bc.tissue = unlist(lapply(strsplit(mca$Cell.name, "_"), function(x) x[1])),
                       cell.type = mca$Annotation,
                       stringsAsFactors = F)

bone.marrow.counts <- read.table("~/Box Sync/Morris Lab/Classifier Analysis/Reference datasets/MCA/MCA_Counts_curated/Bone-Marrow/BoneMarrow1_rm.batch_dge.txt", header = T, row.names = 1, stringsAsFactors = F)
bone.marrow.counts.2 <- read.table("~/Box Sync/Morris Lab/Classifier Analysis/Reference datasets/MCA/MCA_Counts_curated/Bone-Marrow/BoneMarrow2_rm.batch_dge.txt", header = T, row.names = 1, stringsAsFactors = F)
colnames(bone.marrow.counts.2) <- gsub("_4", "_2", colnames(bone.marrow.counts.2))

bone.marrow.counts.3 <- read.table("~/Box Sync/Morris Lab/Classifier Analysis/Reference datasets/MCA/MCA_Counts_curated/Bone-Marrow/BoneMarrow3_rm.batch_dge.txt", header = T, row.names = 1, stringsAsFactors = F)
colnames(bone.marrow.counts.3) <- gsub("_5", "_3", colnames(bone.marrow.counts.3))

bone.marrow.genes <- intersect(intersect(rownames(bone.marrow.counts), rownames(bone.marrow.counts.2)), rownames(bone.marrow.counts.3))

bone.marrow.counts.all <- cbind(cbind(bone.marrow.counts[bone.marrow.genes,], bone.marrow.counts.2[bone.marrow.genes, ]), bone.marrow.counts.3[bone.marrow.genes,])

bone.marrow.m <- read.table("~/Box Sync/Morris Lab/Classifier Analysis/Reference datasets/MCA/MCA_Counts_curated/Bone_Marrow_Mesenchyme/MesenchymalStemCellsPrimary_rm.batch_dge.txt", header = T, row.names = 1, stringsAsFactors = F)

bone.marrow.ckit <- read.table("~/Box Sync/Morris Lab/Classifier Analysis/Reference datasets/MCA/MCA_Counts_curated/Bone-Marrow_c-kit/BoneMarrowcKit1_rm.batch_dge.txt", header = T, row.names = 1, stringsAsFactors = F)
bone.marrow.ckit.2 <- read.table("~/Box Sync/Morris Lab/Classifier Analysis/Reference datasets/MCA/MCA_Counts_curated/Bone-Marrow_c-kit/BoneMarrowcKit2_rm.batch_dge.txt", header = T, row.names = 1, stringsAsFactors = F)
bone.marrow.ckit.3 <- read.table("~/Box Sync/Morris Lab/Classifier Analysis/Reference datasets/MCA/MCA_Counts_curated/Bone-Marrow_c-kit/BoneMarrowcKit3_rm.batch_dge.txt", header = T, row.names = 1, stringsAsFactors = F)

bone.marrow.ckit.genes <- intersect(intersect(rownames(bone.marrow.ckit), rownames(bone.marrow.ckit.2)), rownames(bone.marrow.ckit.3))
bone.marrow.counts.ckit.all <- cbind(cbind(bone.marrow.ckit[bone.marrow.ckit.genes,], bone.marrow.ckit.2[bone.marrow.ckit.genes, ]), bone.marrow.ckit.3[bone.marrow.ckit.genes,])

periphral.blood.1 <- read.csv("~/Box Sync/Morris Lab/Classifier Analysis/Reference datasets/MCA/MCA_Counts_curated/Peripheral_Blood/PeripheralBlood1_rm.batch_dge.txt", sep = "\t", header = T, row.names = 1, stringsAsFactors = F)
periphral.blood.2 <- read.csv("~/Box Sync/Morris Lab/Classifier Analysis/Reference datasets/MCA/MCA_Counts_curated/Peripheral_Blood/PeripheralBlood2_rm.batch_dge.txt", sep = "\t", header = T, row.names = 1, stringsAsFactors = F)
periphral.blood.3 <- read.csv("~/Box Sync/Morris Lab/Classifier Analysis/Reference datasets/MCA/MCA_Counts_curated/Peripheral_Blood/PeripheralBlood3_rm.batch_dge.txt", sep = "\t", header = T, row.names = 1, stringsAsFactors = F)
periphral.blood.4 <- read.csv("~/Box Sync/Morris Lab/Classifier Analysis/Reference datasets/MCA/MCA_Counts_curated/Peripheral_Blood/PeripheralBlood4_rm.batch_dge.txt", sep = "\t", header = T, row.names = 1, stringsAsFactors = F)
periphral.blood.5 <- read.csv("~/Box Sync/Morris Lab/Classifier Analysis/Reference datasets/MCA/MCA_Counts_curated/Peripheral_Blood/PeripheralBlood5_rm.batch_dge.txt", sep = "\t", header = T, row.names = 1, stringsAsFactors = F)
periphral.blood.6 <- read.csv("~/Box Sync/Morris Lab/Classifier Analysis/Reference datasets/MCA/MCA_Counts_curated/Peripheral_Blood/PeripheralBlood6_rm.batch_dge.txt", sep = "\t", header = T, row.names = 1, stringsAsFactors = F)

pb.genes <- intersect(intersect(intersect(rownames(periphral.blood.1), rownames(periphral.blood.2)),
                      intersect(rownames(periphral.blood.3), rownames(periphral.blood.4))),
                      intersect(rownames(periphral.blood.5), rownames(periphral.blood.6)))
pb.counts.all <- cbind(periphral.blood.1[pb.genes, ], periphral.blood.2[pb.genes, ], periphral.blood.3[pb.genes, ],
                       periphral.blood.4[pb.genes, ], periphral.blood.5[pb.genes, ], periphral.blood.6[pb.genes, ])

all.bm.pb.genes <- intersect(intersect(intersect(bone.marrow.genes, bone.marrow.ckit.genes), rownames(bone.marrow.m)), pb.genes)

bone.marrow.all <- cbind(bone.marrow.counts.all[all.bm.pb.genes, ], bone.marrow.m[all.bm.pb.genes, ],
                         bone.marrow.counts.ckit.all[all.bm.pb.genes, ], pb.counts.all[all.bm.pb.genes, ])

bone.marrow.meta <- mca.meta[which(mca.meta$tissue %in% c("Bone-Marrow", "Bone_Marrow_Mesenchyme", "Bone-Marrow_c-kit", "Peripheral_Blood")), ]
bone.marrow.meta.2 <- bone.marrow.meta# mca.meta[which(mca.meta$cell.type %in% final.cell.types),]
  1. Clean up the cell-type labels to create uniform labeling
bone.marrow.meta$cell.type.1 <- bone.marrow.meta$cell.type
bone.marrow.meta$cell.type.1 <- gsub("\\(Bone-Marrow\\)", "", bone.marrow.meta$cell.type.1)
bone.marrow.meta$cell.type.1 <- gsub("\\(Bone_Marrow_Mesenchyme\\)", "", bone.marrow.meta$cell.type.1)
bone.marrow.meta$cell.type.1 <- gsub("\\(Bone-Marrow_c-kit\\)", "", bone.marrow.meta$cell.type.1)
bone.marrow.meta$cell.type.1 <- gsub("\\(Peripheral_Blood\\)", "", bone.marrow.meta$cell.type.1)

bone.marrow.meta.2$cell.type.1 <- bone.marrow.meta.2$cell.type
bone.marrow.meta.2$cell.type.1 <- gsub("\\(Bone-Marrow\\)", "", bone.marrow.meta.2$cell.type.1)
bone.marrow.meta.2$cell.type.1 <- gsub("\\(Bone_Marrow_Mesenchyme\\)", "", bone.marrow.meta.2$cell.type.1)
bone.marrow.meta.2$cell.type.1 <- gsub("\\(Bone-Marrow_c-kit\\)", "", bone.marrow.meta.2$cell.type.1)
bone.marrow.meta.2$cell.type.1 <- gsub("\\(Peripheral_Blood\\)", "", bone.marrow.meta.2$cell.type.1)
bone.marrow.meta.2$cell.type.1[which(bone.marrow.meta.2$cell.type.1 == "Neutrophil ")] <- "Neutrophil"

bone.marrow.meta$cell.type.2 <- unlist(lapply(strsplit(bone.marrow.meta$cell.type.1, "_"), function(x) x[1]))
bone.marrow.meta$cell.type.2[which(bone.marrow.meta$cell.type.2 == "Neutrophil ")] <- "Neutrophil"
bone.marrow.meta$cell.type.2[which(bone.marrow.meta$cell.type.2 == "Monocyte progenitor cell")] <- "Monocyte progenitor"
bone.marrow.meta$cell.type.2[which(bone.marrow.meta$cell.type.2 == "Basophils")] <- "Basophil"
  1. Downsample cells in each tissue by sampling and construct the reference from here.
bm <- rownames(bone.marrow.meta.2)[which(bone.marrow.meta.2$cell.bc.tissue == "BoneMarrow")]
bmck <- rownames(bone.marrow.meta.2)[which(bone.marrow.meta.2$cell.bc.tissue == "BoneMarrowcKit")]
mscp <- rownames(bone.marrow.meta.2)[which(bone.marrow.meta.2$cell.bc.tissue == "MesenchymalStemCellsPrimary")]
pb <- rownames(bone.marrow.meta.2)[which(bone.marrow.meta.2$cell.bc.tissue == "PeripheralBlood")]

sample_list <- c(sample(bm, 2500),
                 sample(bmck, 2500),
                 sample(mscp, 2500),
                 sample(pb, 2500))
  1. The cells are used to construct the reference
meta.sub <- bone.marrow.meta[sample_list, ]
reference.rslt <- construct.high.res.reference(bone.marrow.all, meta.sub, criteria = "cell.type.2", cell.num.for.ref = 90)
saveRDS(reference.rslt, "~/Desktop/reference_paul_15.Rds")

Load pre-generated reference

Here we load the pre-generated reference for this analysis.

reference.rslt <- readRDS("~/Desktop/Reproducibility/Figure 2/Intermediates/Reference/reference_paul_15.Rds")
ref.df <- reference.rslt[[3]]
ref.meta <- reference.rslt[[2]]
ref.sc <- reference.rslt[[1]]

Quadratic Programming

  1. Run Quadratic Programming.
# Measure cell identity in the reference dataset as a background 
single.round.QP.analysis(ref.df, ref.sc, n.cores = 4, save.to.path = "~/Desktop/", save.to.filename = "01_MCA_Based_reference_qp_paul_15", unix.par = TRUE)

# Measure cell identity in the query dataset 
single.round.QP.analysis(ref.df, paul_csv_t, n.cores = 4, save.to.path = "~/Desktop/", save.to.filename = "01_MCA_Based_test_qp_paul_15", unix.par = TRUE, force.eq = 0)
  1. Load the pre-calculated QP results for this analysis.
# Read in background and testing identity scores
background.mtx <- read.csv("~/Desktop/Reproducibility/Figure 2/Intermediates/QP_Outcomes/01_MCA_Based_reference_qp_paul_15_scale.csv", header = T, row.names = 1, stringsAsFactors = F)
mtx.test <- read.csv("~/Desktop/Reproducibility/Figure 2/Intermediates/QP_Outcomes/01_MCA_Based_test_qp_paul_15_scale.csv", header = T, row.names = 1, stringsAsFactors = F)

col.sub <- ncol(background.mtx) - 2

Empirical p-value calculation

To calculate the empirical p-value, run the following two lines. Here we skip these lines to load previously obtained results.

# Conduct reference randomization to get empirical p-value matrix
ref.perc.list <- percentage.calc(background.mtx[,c(1:col.sub)], background.mtx[,c(1:col.sub)])

# Conduct test randomization to get empirical p-value matrix
perc.list <- percentage.calc(as.matrix(mtx.test[,c(1:col.sub)]), as.matrix(background.mtx[,c(1:col.sub)]))

Load the previous empirical p-value data.

# Conduct reference randomization to get empirical p-value matrix
ref.perc.list.all <- readRDS("~/Desktop/Reproducibility/Figure 2/Intermediates/Permutation_Results/paul_15_permutation_rslt.Rds")
ref.perc.list <- ref.perc.list.all$ref
perc.list <- ref.perc.list.all$sample

Initial Classification

We perform initial classification based on quadratic programming metrics: deviance, error, and lagrangian multipliers. Here, we primarily leverage deviance to distinguish unknown, discrete, and hybrid cells.

  1. Construct the ideal deviance distribution based on the background matrix
background.mtx.scale <- as.data.frame(t(apply(background.mtx[,c(1:67)], 1, function(x) x*(1/sum(x)))))
ideal.deviance <- abs(background.mtx.scale[,c(1:67)] - 1/67)
ideal.deviance.all <- rowSums(abs(ideal.deviance))
ideal.deviance.all.mean <- mean(ideal.deviance.all)
ideal.deviance.sd <- sd(ideal.deviance.all)

fit <- fitdistr(ideal.deviance.all, densfun = "normal")

force.test.qp.deviance <- abs(mtx.test[,c(1:67)] - 1/67)

force.test.qp.deviance$total.deviance <- rowSums(force.test.qp.deviance)
mtx.test$deviance <- force.test.qp.deviance[rownames(mtx.test), "total.deviance"]

ideal.deviance.all.mean.sc <- ideal.deviance.all.mean

guessed.multi.id.deviance.mean <- ideal.deviance.all.mean.sc - ideal.deviance.sd * 2
guessed.unknown.deviance.mean <- guessed.multi.id.deviance.mean - ideal.deviance.sd * 2

mtx.test$deviance.p <- pnorm(mtx.test$deviance, mean = ideal.deviance.all.mean.sc, sd = ideal.deviance.sd, lower.tail = T)
mtx.test$deviance.p.multi <- pnorm(mtx.test$deviance, mean = guessed.multi.id.deviance.mean, sd = ideal.deviance.sd *2, lower.tail = T)
mtx.test$deviance.p.unknown <- pnorm(mtx.test$deviance, mean = guessed.unknown.deviance.mean, sd = ideal.deviance.sd/2, lower.tail = T)
  1. Threshold selection
plot(mtx.test$deviance.p.multi, mtx.test$deviance.p)
abline(v = 0.4, col = "red")
abline(h = 0.01, col = "blue")


plot(mtx.test$deviance.p.unknown, mtx.test$deviance.p.multi)
abline(v = 0.01, col = "red")
abline(h = 0.01, col = "blue")

  1. Create the initial classification data frame
init.class <- data.frame(cell.bc = rownames(mtx.test), init.class = "Unknown", stringsAsFactors = F)
rownames(init.class) <- init.class$cell.bc
init.class$init.class <- "Single-ID"
init.class[rownames(mtx.test[which(mtx.test$deviance.p >= 0.01 & mtx.test$deviance.p.multi >= 0.9), ]), "init.class"] <- "Single-ID"
init.class[rownames(mtx.test[which(mtx.test$deviance.p.multi >= 0.4 & mtx.test$deviance.p.unknown >= 0.95 & mtx.test$deviance.p < 0.1), ]), "init.class"] <- "Multi-ID"
init.class[rownames(mtx.test[which(mtx.test$deviance.p.multi < 0.01 & mtx.test$deviance.p.unknown >= 0.01), ]), "init.class"] <- "Unknown"
  1. Assess the initial classification breakdowns
freq.table <- as.data.frame(table(init.class$init.class) * 100/sum(table(init.class$init.class)))
freq.table <- freq.table[order(freq.table$Freq, decreasing = T), ]
freq.table$Var1 <- factor(as.character(freq.table$Var1),
                          levels = as.character(freq.table$Var1),
                          ordered = T)

ggplot(freq.table, aes(x = "Hematopoiesis", y = Freq, fill = Var1)) +
  geom_bar(position = "stack", stat = "identity") +
  scale_fill_brewer(palette = "Paired") +
  theme(legend.position = "right",
        axis.text.x = element_text(face = "bold", size = 12),
        axis.text.y = element_text(face = "bold", size = 12),
        axis.title.x = element_blank(),
        axis.title.y = element_text(face = "bold.italic", size = 14),
        panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank(),
        panel.background = element_blank(), 
        title = element_text(face = "bold.italic", size = 14),
        axis.line = element_line(colour = "black"),
        axis.ticks.x = element_blank())

Binarization and Classification

We generate the binarization matrix so that unknown cells are labelled 0, unknown progenitors -1, and known cell types labelled 1, and perform classification based on the binarized count.

# Binarization of inference results
bin.count <- binarization.mann.whitney(mtx = mtx.test[,c(1:col.sub)], ref.perc.ls = ref.perc.list, ref.meta = ref.meta, perc.ls = perc.list, init.class = init.class)
# Classificationn
classification <- binary.to.classification(bin.count[,c(1:col.sub)])
rownames(classification) <- classification$barcode

At this stage, we have completed classification of the cells in this dataset. Next, we will take a closer look at the Capybara classification outcomes in comparison to the annotation from Paul et al. and PAGA.

Tissues Mapped

  1. We observe where most of the cells mapped to after high-resolution classification. This information is gathered based on the reference meta data.
ref.meta$tissue <- unlist(lapply(strsplit(ref.meta$cell.bc, "_"), function(x) x[1]))
rslt <- as.data.frame(table(ref.meta$cell.type, ref.meta$tissue))
ct.tissue.map <- unique(ref.meta[,c(1,3)])

ct.tissue.map.df <- data.frame()
uniq.ct <- unique(ct.tissue.map$cell.type)

for (uc in uniq.ct) {
  curr.ct.tissue.map <- ct.tissue.map[which(ct.tissue.map$cell.type == uc), ]
  if (nrow(curr.ct.tissue.map) <= 1) {
    curr.df <- curr.ct.tissue.map
  } else {
    freq.sub <- rslt[which(as.character(rslt$Var1) == uc), ]
    major.cont <- as.character(freq.sub$Var2[which(freq.sub$Freq == max(freq.sub$Freq))])
    curr.df <- data.frame(cell.type = uc, tissue = major.cont, stringsAsFactors = F)
  }
  
  if (nrow(ct.tissue.map.df) <= 0) {
    ct.tissue.map.df <- curr.df
  } else {
    ct.tissue.map.df <- rbind(ct.tissue.map.df, curr.df)
  }
}

rownames(ct.tissue.map.df) <- gsub(" ", ".", ct.tissue.map.df$cell.type)
rownames(ct.tissue.map.df) <- gsub("-", ".", rownames(ct.tissue.map.df))
  1. We plot the mapped tissue percentages using a stacked barchart. The color scheme of this differs from the paper as we merged the subcategories under bone marrow.
classification$tissue <- ct.tissue.map.df[classification$call, "tissue"]

freq.table <- as.data.frame(table(classification$tissue) * 100/sum(table(classification$tissue)))
freq.table <- freq.table[order(freq.table$Freq, decreasing = T), ]
freq.table$Var1 <- factor(as.character(freq.table$Var1),
                          levels = as.character(freq.table$Var1),
                          ordered = T)

freq.table$color <- c(RColorBrewer::brewer.pal(12, "Paired")[3],
                      RColorBrewer::brewer.pal(12, "Paired")[7],
                      RColorBrewer::brewer.pal(12, "Paired")[2],
                      RColorBrewer::brewer.pal(12, "Paired")[6])

ggplot(freq.table, aes(x = "Hematopoiesis", y = Freq, fill = Var1)) +
  geom_bar(position = "stack", stat = "identity") +
  scale_fill_manual(values = freq.table$color) +
  theme(legend.position = "right",
        axis.text.x = element_text(face = "bold", size = 12),
        axis.text.y = element_text(face = "bold", size = 12),
        axis.title.x = element_blank(),
        axis.title.y = element_text(face = "bold.italic", size = 14),
        panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank(),
        panel.background = element_blank(), 
        title = element_text(face = "bold.italic", size = 14),
        axis.line = element_line(colour = "black"),
        axis.ticks.x = element_blank())

Overall, we are primarily mapping to bone marrow (BoneMarrowcKit and BoneMarrow are aggregated in the manuscript figure, for simplicity) and some peripheral blood cells. We next group cells and annotate clusters, comparing with Paul et al. and PAGA annotations.

Metadata clean up and grouping

  1. Group Paul & PAGA annotation for a final “ground-truth” annotation
meta.all <- cbind(meta, classification[rownames(meta), ])
meta.all$paul_anno <- meta.all$paul15_clusters
meta.all$paul_anno[which(endsWith(meta.all$paul_anno, "MEP"))] <- "MEP"
meta.all$paul_anno[which(endsWith(meta.all$paul_anno, "GMP"))] <- "GMP"
meta.all$paul_anno[which(endsWith(meta.all$paul_anno, "DC"))] <- "DC"
meta.all$paul_anno[which(endsWith(meta.all$paul_anno, "Baso"))] <- "Baso"
meta.all$paul_anno[which(endsWith(meta.all$paul_anno, "Mo"))] <- "Mo"
meta.all$paul_anno[which(endsWith(meta.all$paul_anno, "Neu"))] <- "Neu"
meta.all$paul_anno[which(endsWith(meta.all$paul_anno, "Eos"))] <- "Eos"
meta.all$paul_anno[which(endsWith(meta.all$paul_anno, "Lymph"))] <- "Lymph"
meta.all$paul_anno[which(endsWith(meta.all$paul_anno, "Ery"))] <- "Ery"
meta.all$paul_anno[which(endsWith(meta.all$paul_anno, "Mk"))] <- "Mk"
  1. Group Capybara annotations together
meta.all$more_gathered_ct <- unlist(lapply(strsplit(meta.all$call, "_"), function(x) x[[1]]))
meta.all$more_gathered_ct[which(meta.all$more_gathered_ct %in% c("Monocyte", "Monocyte.progenitor", "Monocyte.progenitor.cell"))] <- "Monocyte.progenitor"
meta.all$more_gathered_ct[which(meta.all$more_gathered_ct %in% c("Eosinophil.progenitor.cell", "Eosinophils"))] <- "Eosinophils.progenitor"
meta.all$more_gathered_ct[which(meta.all$more_gathered_ct %in% c("Pre.pro.B.cell", "B.cell"))] <- "B.cell.progenitor"
meta.all$more_gathered_ct[which(meta.all$more_gathered_ct %in% c("Multi"))] <- "Hybrid"
meta.all$more_gathered_ct[which(meta.all$more_gathered_ct %in% c("Hematopoietic.stem.progenitor.cell", "Multipotent.progenitor"))] <- "MPP"
  1. Look at frequency of different cell types
freq.table <- as.data.frame(table(meta.all$more_gathered_ct[which(meta.all$more_gathered_ct != "Hybrid")]) * 100/sum(table(meta.all$more_gathered_ct[which(meta.all$more_gathered_ct != "Hybrid")])))
freq.table <- freq.table[order(freq.table$Freq, decreasing = T), ]
freq.table$Var1 <- factor(as.character(freq.table$Var1),
                          levels = as.character(freq.table$Var1),
                          ordered = T)

freq.table.sub <- freq.table[which(freq.table$Freq > 1.5),]
freq.table.sub <- rbind(freq.table.sub, data.frame(Var1 = "Other", Freq = (100 - sum(freq.table.sub$Freq))))

ggplot(freq.table.sub, aes(x = Var1, y = Freq, fill = Var1)) +
  geom_bar(position = "dodge", stat = "identity") +
  scale_fill_viridis_d(option = "A", begin = 0.15, end = 0.85) +
  theme(legend.position = "none",
        axis.text.x = element_text(face = "bold", size = 12, angle = 90, hjust = 1),
        axis.text.y = element_text(face = "bold", size = 12),
        axis.title.x = element_blank(),
        axis.title.y = element_text(face = "bold.italic", size = 14),
        panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank(),
        panel.background = element_blank(), 
        title = element_text(face = "bold.italic", size = 14),
        axis.line = element_line(colour = "black"),
        axis.ticks.x = element_blank())

Cluster Labels and Comparison to Previous Labels

Here we label the clusters based on the most represented population in each cluster.

  1. Assign cluster labels based on PAGA annotation
  2. Assign cluster labels based on Capybara annotation
meta.all$louvain.label <- NA
meta.all$louvain.label[which(meta.all$louvain %in% c(16))] <- "Stem"
meta.all$louvain.label[which(meta.all$louvain %in% c(10,17,5,3))] <- "Ery0"
meta.all$louvain.label[which(meta.all$louvain %in% c(15, 6))] <- "Ery1"
meta.all$louvain.label[which(meta.all$louvain %in% c(18))] <- "Ery2"
meta.all$louvain.label[which(meta.all$louvain %in% c(13))] <- "Ery3"
meta.all$louvain.label[which(meta.all$louvain %in% c(7,12))] <- "Ery4"

meta.all$louvain.label[which(meta.all$louvain %in% c(20, 8))] <- "MEP"
meta.all$louvain.label[which(meta.all$louvain %in% c(4,0))] <- "GMP"
meta.all$louvain.label[which(meta.all$louvain %in% c(22))] <- "Baso"

meta.all$louvain.label[which(meta.all$louvain %in% c(19,14,2))] <- "Neu"
meta.all$louvain.label[which(meta.all$louvain %in% c(24,9,1,11))] <- "Mo"
meta.all$louvain.label[which(meta.all$louvain %in% c(23))] <- "DC"
meta.all$louvain.label[which(meta.all$louvain %in% c(21))] <- "Lymph"

meta.all$capy.cluster.label <- NA
meta.all$capy.cluster.label[which(meta.all$louvain %in% c(0,16))] <- "MPP/HSPC"
meta.all$capy.cluster.label[which(meta.all$louvain %in% c(1,9,11,24))] <- "Monocyte.progenitor"
meta.all$capy.cluster.label[which(meta.all$louvain %in% c(2,14))] <- "Neutrophil"
meta.all$capy.cluster.label[which(meta.all$louvain %in% c(3,5,6,7,12,13,18,15))] <- "Erythrocyte.progenitor"
meta.all$capy.cluster.label[which(meta.all$louvain %in% c(4))] <- meta.all$more_gathered_ct[which(meta.all$louvain %in% c(4))]
meta.all$capy.cluster.label[which(meta.all$louvain %in% c(8,20))] <- meta.all$more_gathered_ct[which(meta.all$louvain %in% c(8,20))]

meta.all$capy.cluster.label[which(meta.all$louvain %in% c(10, 17))] <- "Erythroblast"
meta.all$capy.cluster.label[which(meta.all$louvain %in% c(19))] <- meta.all$more_gathered_ct[which(meta.all$louvain %in% c(19))]
meta.all$capy.cluster.label[which(meta.all$louvain %in% c(22))] <- "Basophil"
meta.all$capy.cluster.label[which(meta.all$louvain %in% c(21))] <- "NK.cell"
meta.all$capy.cluster.label[which(meta.all$louvain %in% c(23))] <- "DC"
  1. Here we further clean up the hybrid cells to produce the complete list of cells with discrete identities.
multi.classification.list <- multi.id.curate.qp(binary.counts = bin.count, classification = classification, qp.matrix = mtx.test)
Using cell.bc as id variables
# Reassign variables
actual.multi <- multi.classification.list[[1]]
new.classification <- multi.classification.list[[2]]
colnames(new.classification) <- c("bc", "new.call")
  1. Add the corrected classifcation to the meta data
meta.all <- cbind(meta.all, new.classification[rownames(meta.all), ])
meta.all$more_gathered_ct_new <- unlist(lapply(strsplit(meta.all$new.call, "_"), function(x) x[[1]]))
meta.all$more_gathered_ct_new[which(meta.all$more_gathered_ct_new %in% c("Monocyte", "Monocyte.progenitor", "Monocyte.progenitor.cell"))] <- "Monocyte.progenitor"
meta.all$more_gathered_ct_new[which(meta.all$more_gathered_ct_new %in% c("Eosinophil.progenitor.cell", "Eosinophils"))] <- "Eosinophils.progenitor"
meta.all$more_gathered_ct_new[which(meta.all$more_gathered_ct_new %in% c("Pre.pro.B.cell", "B.cell"))] <- "B.cell.progenitor"
meta.all$more_gathered_ct_new[which(meta.all$more_gathered_ct_new %in% c("Multi"))] <- "Multi_ID"
meta.all$more_gathered_ct_new[which(meta.all$more_gathered_ct_new %in% c("Multipotent.progenitor","Hematopoietic.stem.progenitor.cell"))] <- "MPP/HSPC"
  1. To assess the discrete cells only, we remove the hybrid cells for now.
meta.all.no.multi <- meta.all[which(meta.all$new.call != "Multi_ID"), ]
meta.all.table <- table(meta.all.no.multi$capy.cluster.label, meta.all.no.multi$louvain.label)
meta.all.table <- as.data.frame(apply(meta.all.table, 2, function(x) round(x *100/sum(x), digits = 3)))
meta.all.table$capy.call <- rownames(meta.all.table) 
  1. Heatmap plot and comparison
meta.melt <- reshape2::melt(meta.all.table)
Using capy.call as id variables
meta.melt <- meta.melt[which(meta.melt$capy.call != "Hybrid"),]
meta.melt <- meta.melt[which(meta.melt$capy.call != "MPP"),]

meta.melt$variable <- factor(meta.melt$variable,
                              levels = c("Ery0", "Ery1", "Ery2","Ery3", "Ery4","MEP",
                                         "Baso", "Mo", "Neu", "Lymph", "GMP",  "Stem", "DC"),
                             ordered = T)

meta.melt$capy.call <- factor(meta.melt$capy.call,
                              levels = c("Erythroblast", "Erythrocyte.progenitor", "Megakaryocyte.progenitor.cell",
                                         "Basophil", "Monocyte.progenitor", "Neutrophil", "NK.cell", "MPP/HSPC",
                                         "DC", "B.cell.progenitor", "Eosinophils.progenitor"),
                              ordered = T)

ggplot(meta.melt, aes(x = variable, y = capy.call, fill = value)) +
  geom_tile() +
  scale_fill_viridis_c(option = "A", name = "percentage", begin = 0.15, end = 0.85) +
  ggtitle("Paul et al. 2015") +
  labs(x = "Original Annotation", y = "Capybara Annotation") +
  theme(legend.position="bottom",
        axis.text.x = element_text(angle = 90, hjust =1, face = "bold", size = 12),
        axis.text.y = element_text(face = "bold", size = 12),
        axis.title.x = element_text(face = "bold.italic", size = 14),
        axis.title.y = element_text(face = "bold.italic", size = 14),
        panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank(),
        panel.background = element_blank(), 
        title = element_text(face = "bold.italic", size = 14),
        axis.line = element_blank(),
        axis.ticks = element_blank())

Overall, we are corresponding well to the Paul et al & PAGA annotation. We next carefully assess the hybrid cells, leveraging pseudotime information from PAGA.

Pseudotime

Discrete Identities

First, we check the pseudotime of the discrete cell types classified in the dataset as another benchmarking metric to evaluate the efficacy of the classification, where we see HSPCs occupuing the earliest pseudotime.

median.quartile <- function(x){
  out <- quantile(x, probs = c(0.25,0.5,0.75))
  names(out) <- c("ymin","y","ymax")
  return(out) 
}
meta.sub.for.pseudotime <- meta.all[-which(meta.all$more_gathered_ct_new %in% c("Dendritic.cell", "NK.cell", "Multi_ID", "B.cell.progenitor")), ]
meta.sub.for.pseudotime$more_gathered_ct_new <- factor(meta.sub.for.pseudotime$more_gathered_ct_new,
                                                   levels = c("MPP/HSPC", 
                                                              "Megakaryocyte.progenitor.cell", "Basophil", "Eosinophils.progenitor",
                                                              "Monocyte.progenitor", "Neutrophil", "Macrophage",
                                                              "Erythrocyte.progenitor", "Erythroblast"),
                                                   ordered = T)
cs <- viridis(20)
ggplot(meta.sub.for.pseudotime, aes(x = more_gathered_ct_new, y = dpt_pseudotime, fill = more_gathered_ct_new)) +
  geom_violin(scale = "width") +
  stat_summary(fun.y=median.quartile,geom='point', color = rep(cs[c(20, 20, 20, 20, 20, 1,1,1,1)], each = 3)) +
  stat_summary(fun.y=median.quartile,geom='line', color = rep(cs[c(20, 20, 20, 20, 20, 1,1,1,1)], each = 3)) +
  geom_jitter(color = "grey", size = 0.1) +
  scale_fill_viridis_d(option = "A") +
  coord_flip() +
  theme(legend.position="none",
        axis.text.x = element_text(face = "bold.italic"),
        axis.text.y = element_text(face = "bold"),
        axis.title.x = element_blank(),
        axis.title.y = element_blank(),
        panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank(),
        panel.background = element_blank(), 
        title = element_text(face = "bold.italic", size = 14),
        axis.line = element_line(colour = "black", size = 1))

Hybrid Cells

Ideally, hybrid cells should have a pseudotime range in between their origin and destination cell states. Therefore, we investigate the pseudotime distribution of these cells and their discrete counterparts.

  1. We first input the pseudotime for the discrete cell types that compose the hybrids in a data frame
pseudotime.for.each.category <- meta.all[-which(meta.all$more_gathered_ct_new == "Multi_ID" | meta.all$dpt_pseudotime == Inf), ]
pseudotime.dt <- pseudotime.for.each.category[,c(5,16,20)]
mean.pseudotime.dt <- c()
unique.ct <- unique(pseudotime.for.each.category$more_gathered_ct_new)
for (ct in unique.ct) {
  mean.pseudotime.dt[ct] <- mean(pseudotime.for.each.category[which(pseudotime.for.each.category$more_gathered_ct_new == ct), "dpt_pseudotime"])
}
  1. We next isolate pseudotime for the hybrids
ct.pseudo <- as.data.frame(mean.pseudotime.dt)
multi.id.pseudo <- actual.multi
multi.id.pseudo$pseudotime <- meta.all[actual.multi$cell.bc, "dpt_pseudotime"]
multi.id.pseudo <- multi.id.pseudo[-which(multi.id.pseudo$pseudotime == Inf),]
multi.id.pseudo$ct.only <- gsub("frxn_cell.type_", "", multi.id.pseudo$variable)
multi.id.pseudo$ct.only <- unlist(lapply(strsplit(multi.id.pseudo$ct.only, "_"), function(x) x[1]))

multi.id.pseudo$ct.only.avg.pseudo <- ct.pseudo[multi.id.pseudo$ct.only, "mean.pseudotime.dt"]
multi.id.pseudo <- multi.id.pseudo[!is.na(multi.id.pseudo$ct.only.avg.pseudo), ]
  1. More detailed break down of the hybrids
cell.table <- data.frame()
cell.uniq <- unique(multi.id.pseudo$cell.bc)
for (curr.c in cell.uniq) {
  ct <- multi.id.pseudo[which(multi.id.pseudo$cell.bc == curr.c), "ct.only"]
  ct[which(ct == "Monocyte")] <- "Monocyte.progenitor"
  
  if (length(unique(ct)) > 1 &
      length(unique(ct)) == 2) {
    
    curr.df <- data.frame(cell.bc = curr.c,
                          identity = paste0(sort(unique(ct)), collapse = "-"),
                          pseudo = mean(multi.id.pseudo[which(multi.id.pseudo$cell.bc == curr.c), "pseudotime"]),
                          min.range = min(multi.id.pseudo[which(multi.id.pseudo$cell.bc == curr.c), "ct.only.avg.pseudo"]),
                          max.range = max(multi.id.pseudo[which(multi.id.pseudo$cell.bc == curr.c), "ct.only.avg.pseudo"]),
                          stringsAsFactors = F)
    
    if (nrow(cell.table) <= 0) {
      cell.table <- curr.df
    } else {
      cell.table <- rbind(cell.table, curr.df)
    }
  }
}
  1. Identify the major hybrid populations.
freq.table.new <- as.data.frame(table(cell.table$identity))
freq.table.new <- freq.table.new[order(freq.table.new$Freq, decreasing = T), ]

freq.table <- as.data.frame(table(cell.table$identity) * 100/sum(table(cell.table$identity)))
freq.table <- freq.table[order(freq.table$Freq, decreasing = T), ]
freq.table$Var1 <- factor(as.character(freq.table$Var1),
                          levels = as.character(freq.table$Var1),
                          ordered = T)

freq.table.sub <- freq.table[which(freq.table$Freq > 1000/257),]
freq.table.sub$Freq <- freq.table.sub$Freq * 100/sum(freq.table.sub$Freq)

ggplot(freq.table.sub, aes(x = Var1, y = Freq, fill = Var1)) +
  geom_bar(position = "dodge", stat = "identity") +
  scale_fill_viridis_d(option = "A", begin = 0.15, end = 0.85) +
  theme(legend.position = "none",
        axis.text.x = element_text(face = "bold", size = 12, angle = 90, hjust = 1),
        axis.text.y = element_text(face = "bold", size = 12),
        axis.title.x = element_blank(),
        axis.title.y = element_text(face = "bold.italic", size = 14),
        panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank(),
        panel.background = element_blank(), 
        title = element_text(face = "bold.italic", size = 14),
        axis.line = element_line(colour = "black"),
        axis.ticks.x = element_blank())

  1. Filter the hybrids to the five major populations for the pseudotime comparison
cell.table.sub <- cell.table[which(cell.table$identity %in% c("Erythroblast-Erythrocyte.progenitor", "Monocyte.progenitor-Neutrophil",
                                                              "Erythrocyte.progenitor-Megakaryocyte.progenitor.cell", 
                                                              "Eosinophils-Monocyte.progenitor", "Eosinophils-Megakaryocyte.progenitor.cell")), ]
meta.all.multi.cells <- meta.all[which(rownames(meta.all) %in% cell.table.sub$cell.bc), ]
rownames(cell.table.sub) <- cell.table.sub$cell.bc
meta.all.multi.cells$multi.break.down <- cell.table.sub[rownames(meta.all.multi.cells), "identity"]
  1. We first look at the largest hybrid population: erythroblast-erythrocyte progenitor hybrids
unq.ct <- unique(meta.all$more_gathered_ct_new)
label_df <- data.frame()
for (curr.ct in unq.ct) {
  curr_sub <- meta.all[which(meta.all$more_gathered_ct_new == curr.ct),]
  curr_v1 <- mean(curr_sub$V1)
  curr_v2 <- mean(curr_sub$V2)
  curr_df <- data.frame(V1 = curr_v1, V2 = curr_v2, cell.type = curr.ct, stringsAsFactors = F)
  if (nrow(label_df) <= 0) {
    label_df <- curr_df
  } else {
    label_df <- rbind(label_df, curr_df)
  }
}
label_df_no_multi <- label_df[-which(label_df$cell.type == "Multi_ID"),]
meta.all$cell.type <- meta.all$more_gathered_ct_new
meta.all[, "ery.ery.multi"] <- 0
meta.all[cell.table[which(cell.table$identity == "Erythroblast-Erythrocyte.progenitor"), "cell.bc"], "ery.ery.multi"] <- 1
library(ggforce)
ggplot(label_df_no_multi[which(label_df_no_multi$cell.type %in% c("Erythroblast", "Erythrocyte.progenitor")),], aes(x = V1, y = V2, label = cell.type, color = cell.type)) +
  geom_point(data = meta.all, color = "lightgrey") +
  geom_point(data = meta.all[which(meta.all$more_gathered_ct_new %in% c("Erythroblast", "Erythrocyte.progenitor")),], aes(color = more_gathered_ct_new)) +
  geom_circle(data = meta.all[which(meta.all$ery.ery.multi == 1), ], mapping = aes(x0 = V1, y0 = V2, r = 100), fill = "darkgrey", inherit.aes = F) +
  scale_color_manual(values = RColorBrewer::brewer.pal(12, "Paired")[c(5,6)]) +
  geom_text_repel(box.padding = 0.5, max.overlaps = Inf, color = "black") +
  #geom_point() + 
  labs(x = "FA1", y = "FA2") +
  ggtitle("Capybara Annotation: Erythroblast & Erythrocyte Progenitor") +
  theme(legend.position="none",
        axis.text.x = element_blank(),
        axis.text.y = element_blank(),
        axis.title = element_text(face = "bold.italic", size = 14), 
        panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank(),
        panel.background = element_blank(), 
        title = element_text(face = "bold.italic", size = 14),
        axis.line = element_line(colour = "black", size = 0.5),
        axis.ticks = element_blank())

  1. We next look at all the other hybrids
  1. Monocyte.progenitor-Neutrophil hybrids
ggplot(label_df_no_multi[which(label_df_no_multi$cell.type %in% c("Monocyte.progenitor", "Neutrophil")),], aes(x = V1, y = V2, label = cell.type, color = cell.type)) +
  geom_point(data = meta.all, color = "lightgrey") +
  geom_point(data = meta.all[which(meta.all$more_gathered_ct_new %in% c("Monocyte.progenitor", "Neutrophil")),], aes(color = more_gathered_ct_new)) +
  geom_circle(data = meta.all[cell.table[which(cell.table$identity == "Monocyte.progenitor-Neutrophil"), "cell.bc"], ], mapping = aes(x0 = V1, y0 = V2, r = 100), fill = "darkgrey", inherit.aes = F) +
  scale_color_manual(values = RColorBrewer::brewer.pal(12, "Paired")[c(5,6)]) +
  geom_text_repel(box.padding = 0.5, max.overlaps = Inf, color = "black") +
  #geom_point() + 
  labs(x = "FA1", y = "FA2") +
  ggtitle("Capybara Annotation: Monocyte Progenitor & Neutrophil") +
  theme(legend.position="none",
        axis.text.x = element_blank(),
        axis.text.y = element_blank(),
        axis.title = element_text(face = "bold.italic", size = 14), 
        panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank(),
        panel.background = element_blank(), 
        title = element_text(face = "bold.italic", size = 14),
        axis.line = element_line(colour = "black", size = 0.5),
        axis.ticks = element_blank())

  1. Erythrocyte.progenitor-Megakaryocyte.progenitor.cell hybrids
ggplot(label_df_no_multi[which(label_df_no_multi$cell.type %in% c("Erythrocyte.progenitor", "Megakaryocyte.progenitor.cell")),], aes(x = V1, y = V2, label = cell.type, color = cell.type)) +
  geom_point(data = meta.all, color = "lightgrey") +
  geom_point(data = meta.all[which(meta.all$more_gathered_ct_new %in% c("Erythrocyte.progenitor", "Megakaryocyte.progenitor.cell")),], aes(color = more_gathered_ct_new)) +
  geom_circle(data = meta.all[cell.table[which(cell.table$identity == "Erythrocyte.progenitor-Megakaryocyte.progenitor.cell"), "cell.bc"], ], mapping = aes(x0 = V1, y0 = V2, r = 100), fill = "darkgrey", inherit.aes = F) +
  scale_color_manual(values = RColorBrewer::brewer.pal(12, "Paired")[c(5,6)]) +
  geom_text_repel(box.padding = 0.5, max.overlaps = Inf, color = "black") +
  #geom_point() + 
  labs(x = "FA1", y = "FA2") +
  ggtitle("Capybara Annotation: Megakaryocyte Progenitor & Erythrocyte Progenitor") +
  theme(legend.position="none",
        axis.text.x = element_blank(),
        axis.text.y = element_blank(),
        axis.title = element_text(face = "bold.italic", size = 14), 
        panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank(),
        panel.background = element_blank(), 
        title = element_text(face = "bold.italic", size = 14),
        axis.line = element_line(colour = "black", size = 0.5),
        axis.ticks = element_blank())

  1. Eosinophils.progenitor-Megakaryocyte.progenitor.cell hybrids
ggplot(label_df_no_multi[which(label_df_no_multi$cell.type %in% c("Eosinophils.progenitor", "Megakaryocyte.progenitor.cell")),], aes(x = V1, y = V2, label = cell.type, color = cell.type)) +
  geom_point(data = meta.all, color = "lightgrey") +
  geom_point(data = meta.all[which(meta.all$more_gathered_ct_new %in% c("Eosinophils.progenitor", "Megakaryocyte.progenitor.cell")),], aes(color = more_gathered_ct_new)) +
  geom_circle(data = meta.all[cell.table[which(cell.table$identity == "Eosinophils-Megakaryocyte.progenitor.cell"), "cell.bc"], ], mapping = aes(x0 = V1, y0 = V2, r = 100), fill = "darkgrey", inherit.aes = F) +
  scale_color_manual(values = RColorBrewer::brewer.pal(12, "Paired")[c(5,6)]) +
  geom_text_repel(box.padding = 0.5, max.overlaps = Inf, color = "black") +
  #geom_point() + 
  labs(x = "FA1", y = "FA2") +
  ggtitle("Capybara Annotation: Megakaryocyte Progenitor & Eosinophil Progenitor") +
  theme(legend.position="none",
        axis.text.x = element_blank(),
        axis.text.y = element_blank(),
        axis.title = element_text(face = "bold.italic", size = 14), 
        panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank(),
        panel.background = element_blank(), 
        title = element_text(face = "bold.italic", size = 14),
        axis.line = element_line(colour = "black", size = 0.5),
        axis.ticks = element_blank())

  1. Eosinophils.progenitor-Monocyte.progenitor hybrids
ggplot(label_df_no_multi[which(label_df_no_multi$cell.type %in% c("Eosinophils.progenitor", "Monocyte.progenitor")),], aes(x = V1, y = V2, label = cell.type, color = cell.type)) +
  geom_point(data = meta.all, color = "lightgrey") +
  geom_point(data = meta.all[which(meta.all$more_gathered_ct_new %in% c("Eosinophils.progenitor", "Monocyte.progenitor")),], aes(color = more_gathered_ct_new)) +
  geom_circle(data = meta.all[cell.table[which(cell.table$identity == "Eosinophils-Monocyte.progenitor"), "cell.bc"], ], mapping = aes(x0 = V1, y0 = V2, r = 100), fill = "darkgrey", inherit.aes = F) +
  scale_color_manual(values = RColorBrewer::brewer.pal(12, "Paired")[c(5,6)]) +
  geom_text_repel(box.padding = 0.5, max.overlaps = Inf, color = "black") +
  #geom_point() + 
  labs(x = "FA1", y = "FA2") +
  ggtitle("Capybara Annotation: Eosinophil Progenitor & Monocyte Progenitor") +
  theme(legend.position="none",
        axis.text.x = element_blank(),
        axis.text.y = element_blank(),
        axis.title = element_text(face = "bold.italic", size = 14), 
        panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank(),
        panel.background = element_blank(), 
        title = element_text(face = "bold.italic", size = 14),
        axis.line = element_line(colour = "black", size = 0.5),
        axis.ticks = element_blank())

  1. We next plot violin plots to compare hybrid and discrete cell pseudotime.
library(ggpubr)

Attaching package: ‘ggpubr’

The following object is masked from ‘package:plyr’:

    mutate
multi.meta.pseudotime <- meta.all.multi.cells[,c(5,16,20)]
colnames(multi.meta.pseudotime)[3] <- "more_gathered_ct_new"
multi.meta.pseudotime$category <- "multis"
rownames(cell.table.sub) <- cell.table.sub$cell.bc
multi.meta.pseudotime$more_gathered_ct_new <- cell.table.sub[rownames(multi.meta.pseudotime), "identity"]
pseudotime.dt$category <- "ends"
combined.to.plot <- rbind(multi.meta.pseudotime, pseudotime.dt)

combined.to.plot$new.cat.1 <- NA
combined.to.plot$new.cat.2 <- NA
combined.to.plot$new.cat.3 <- NA
combined.to.plot$new.cat.4 <- NA
combined.to.plot$new.cat.5 <- NA
combined.to.plot$new.cat.6 <- NA

combined.to.plot[which(combined.to.plot$more_gathered_ct_new %in% c("Erythroblast", "Erythrocyte.progenitor", "Erythroblast-Erythrocyte.progenitor")), "new.cat.1"] <-"Erythroblast-Erythrocyte.progenitor"

combined.to.plot$more_gathered_ct_new[which(combined.to.plot$more_gathered_ct_new=="Monocyte-Neutrophil")] <- "Monocyte.progenitor-Neutrophil"
combined.to.plot[which(combined.to.plot$more_gathered_ct_new %in% c("Monocyte.progenitor","Neutrophil", "Monocyte.progenitor-Neutrophil")), "new.cat.2" ] <-"Monocyte.progenitor-Neutrophil"

combined.to.plot[which(combined.to.plot$more_gathered_ct_new %in% c("Erythrocyte.progenitor-Megakaryocyte.progenitor.cell", "Erythrocyte.progenitor","Megakaryocyte.progenitor.cell")), "new.cat.4" ] <-"Erythrocyte.progenitor-Megakaryocyte.progenitor.cell"

combined.to.plot[which(combined.to.plot$more_gathered_ct_new %in% c("Eosinophils-Monocyte.progenitor", "Eosinophils.progenitor", "Monocyte.progenitor")), "new.cat.5"] <-"Eosinophils.progenitor-Monocyte.progenitor"

combined.to.plot[which(combined.to.plot$more_gathered_ct_new %in% c("Eosinophils-Megakaryocyte.progenitor.cell", "Eosinophils.progenitor", "Megakaryocyte.progenitor.cell")), "new.cat.6" ] <-"Eosinophils-Megakaryocyte.progenitor.cell"
cs <- viridis(20, option = "A", begin = 0.15, end = 0.85)

my_comparisons <- list( c("Erythroblast", "Erythroblast-Erythrocyte.progenitor"), c("Erythrocyte.progenitor", "Erythroblast-Erythrocyte.progenitor"))
ggplot(combined.to.plot[!is.na(combined.to.plot$new.cat.1), ], aes(x = more_gathered_ct_new, y = dpt_pseudotime, fill = category)) +
  geom_violin() +
  geom_jitter(color = "black", size = 0.8) +
  scale_fill_viridis_d(option = "A", begin = 0.15, end = 0.85) +
  stat_summary(fun.y=median.quartile,geom='point', color = rep(cs[c(20,1,20)], each = 3)) +
  stat_summary(fun.y=median.quartile,geom='line', color = rep(cs[c(20,1,20)], each = 3)) +
  stat_compare_means(comparisons = my_comparisons, label = "..p.signif..") +
  labs(y = "pseudotime") +
  ggtitle("Erythroblast-Erythrocyte.progenitor") + 
  theme(legend.position="none",
        axis.text.x = element_text(size = 12),
        axis.text.y = element_text(size = 12),
        axis.title.y = element_text(size = 14, face = "bold.italic"),
        axis.title.x = element_blank(),
        title = element_text(size = 16, face = "bold.italic"),
        panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank(),
        panel.background = element_blank(), 
        axis.line = element_line(colour = "black"))


my_comparisons <- list( c("Monocyte.progenitor", "Monocyte.progenitor-Neutrophil"), c("Neutrophil", "Monocyte.progenitor-Neutrophil"))
ggplot(combined.to.plot[!is.na(combined.to.plot$new.cat.2), ], aes(x = more_gathered_ct_new, y = dpt_pseudotime, fill = category)) +
  geom_violin() +
  geom_jitter(color = "black", size = 0.8) +
  scale_fill_viridis_d(option = "A", begin = 0.15, end = 0.85) +stat_summary(fun.y=median.quartile,geom='point', color = rep(cs[c(20,1,20)], each = 3)) +
  stat_summary(fun.y=median.quartile,geom='line', color = rep(cs[c(20,1,20)], each = 3)) +
  stat_compare_means(comparisons = my_comparisons, label = "..p.signif..") +
  labs(y = "pseudotime") +
  ggtitle("Monocyte.progenitor-Neutrophil") +
  theme(legend.position="none",
        axis.text.x = element_text(size = 12),
        axis.text.y = element_text(size = 12),
        axis.title.y = element_text(size = 14, face = "bold.italic"),
        axis.title.x = element_blank(),
        title = element_text(size = 16, face = "bold.italic"),
        panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank(),
        panel.background = element_blank(), 
        axis.line = element_line(colour = "black"))


my_comparisons <- list( c("Megakaryocyte.progenitor.cell", "Erythrocyte.progenitor-Megakaryocyte.progenitor.cell"), c("Erythrocyte.progenitor", "Erythrocyte.progenitor-Megakaryocyte.progenitor.cell"))
ggplot(combined.to.plot[!is.na(combined.to.plot$new.cat.4), ], aes(x = more_gathered_ct_new, y = dpt_pseudotime, fill = category)) +
  geom_violin() +
  geom_jitter(color = "black", size = 0.8) +
  scale_fill_viridis_d(option = "A", begin = 0.15, end = 0.85) +
  stat_summary(fun.y=median.quartile,geom='point', color = rep(cs[c(20,1,20)], each = 3)) +
  stat_summary(fun.y=median.quartile,geom='line', color = rep(cs[c(20,1,20)], each = 3)) +
  stat_compare_means(comparisons = my_comparisons, label = "..p.signif..") +
  labs(y = "pseudotime") +
  ggtitle("Erythrocyte.progenitor-Megakaryocyte.progenitor.cell") + 
  theme(legend.position="none",
        axis.text.x = element_text(size = 12),
        axis.text.y = element_text(size = 12),
        axis.title.y = element_text(size = 14, face = "bold.italic"),
        axis.title.x = element_blank(),
        title = element_text(size = 16, face = "bold.italic"),
        panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank(),
        panel.background = element_blank(), 
        axis.line = element_line(colour = "black"))


my_comparisons <- list( c("Megakaryocyte.progenitor.cell", "Eosinophils-Megakaryocyte.progenitor.cell"), c("Eosinophils.progenitor", "Eosinophils-Megakaryocyte.progenitor.cell"))
eos.mk <- combined.to.plot[!is.na(combined.to.plot$new.cat.6), ]
eos.mk$more_gathered_ct_new <- factor(eos.mk$more_gathered_ct_new, levels = c("Eosinophils.progenitor", "Eosinophils-Megakaryocyte.progenitor.cell", "Megakaryocyte.progenitor.cell"), ordered = T)
ggplot(eos.mk, aes(x = more_gathered_ct_new, y = dpt_pseudotime, fill = category)) +
  geom_violin() +
  geom_jitter(color = "black", size = 0.8) +
  scale_fill_viridis_d(option = "A", begin = 0.15, end = 0.85) +
  stat_summary(fun.y=median.quartile,geom='point', color = rep(cs[c(20,1,20)], each = 3)) +
  stat_summary(fun.y=median.quartile,geom='line', color = rep(cs[c(20,1,20)], each = 3)) +
  stat_compare_means(comparisons = my_comparisons, label = "..p.signif..") +
  labs(y = "pseudotime") +
  ggtitle("Eosinophils-Megakaryocyte.progenitor.cell") + 
  theme(legend.position="none",
        axis.text.x = element_text( size = 12),
        axis.text.y = element_text(size = 12),
        axis.title.y = element_text(size = 14, face = "bold.italic"),
        axis.title.x = element_blank(),
        title = element_text(size = 14, face = "bold.italic"),
        panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank(),
        panel.background = element_blank(), 
        axis.line = element_line(colour = "black"))


my_comparisons <- list( c("Monocyte.progenitor", "Eosinophils-Monocyte.progenitor"), c("Eosinophils.progenitor", "Eosinophils-Monocyte.progenitor"))
eos.mono <- combined.to.plot[!is.na(combined.to.plot$new.cat.5), ]
eos.mono$more_gathered_ct_new <- factor(eos.mono$more_gathered_ct_new, levels = c("Eosinophils.progenitor", "Eosinophils-Monocyte.progenitor", "Monocyte.progenitor"), ordered = T)
ggplot(eos.mono, aes(x = more_gathered_ct_new, y = dpt_pseudotime, fill = category)) +
  geom_violin() +
  geom_jitter(color = "black", size = 0.8) +
  scale_fill_viridis_d(option = "A", begin = 0.15, end = 0.85) +
  stat_summary(fun.y=median.quartile,geom='point', color = rep(cs[c(20,1,20)], each = 3)) +
  stat_summary(fun.y=median.quartile,geom='line', color = rep(cs[c(20,1,20)], each = 3)) +
  stat_compare_means(comparisons = my_comparisons, label = "..p.signif..") +
  labs(y = "pseudotime") +
  ggtitle("Eosinophils-Monocyte.progenitor") + 
  theme(legend.position="none",
        axis.text.x = element_text(size = 12),
        axis.text.y = element_text(size = 12),
        axis.title.y = element_text(size = 14, face = "bold.italic"),
        axis.title.x = element_blank(),
        title = element_text(size = 14, face = "bold.italic"),
        panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank(),
        panel.background = element_blank(), 
        axis.line = element_line(colour = "black"))

NA
NA

Overall, hybrid cells occupy intermediate pseudotime, between discrete cell states.

Transition score vs Connectivity Matrix from PAGA

We next compare connectivity scores from PAGA to support our transition metric.

  1. We calculate the transition scores for these cells
scores <- transition.score(actual.multi)
  1. We load the connectivity matrix from PAGA
connectivity <- read.table("~/Desktop/Reproducibility/Figure 2/Intermediates/Data/connectivity_mtx.txt")
rownames(connectivity) <- rownames(meta.all)
colnames(connectivity) <- rownames(meta.all)
  1. We compute the connectivity score based on the connectivity matrix. The score is calculated based on the within-cluster cell-to-cell connectivity and the cross-cluster cell-to-cell connectivity.
in.cell.type.connectivity.score <- c()
out.cell.type.connectivity.score <- c()
for (i in 1:nrow(scores)) {
  curr.ct <- rownames(scores)[i]
  cells.in.cell.ty <- rownames(meta.all)[which(meta.all$new.call == curr.ct)]
  if (length(cells.in.cell.ty) > 0) {
    in.cell.type.connectivity.score[curr.ct] <- sum(connectivity[cells.in.cell.ty, cells.in.cell.ty])
    out.cell.type.connectivity.score[curr.ct] <- sum(connectivity[cells.in.cell.ty, which(!colnames(connectivity) %in% cells.in.cell.ty)])
  }
}

in.cell.type.connectivity.score <- as.data.frame(in.cell.type.connectivity.score)
colnames(in.cell.type.connectivity.score) <- "In.Cell.Type"
in.cell.type.connectivity.score$Out.Cell.Type <- out.cell.type.connectivity.score[rownames(in.cell.type.connectivity.score)]
in.cell.type.connectivity.score$transition.score <- scores[rownames(in.cell.type.connectivity.score), "entropy"]
  1. Plot to assess correlation
ggplot(in.cell.type.connectivity.score, aes(x = log1p(transition.score), y = log1p(Out.Cell.Type))) +
  geom_point() +
  theme(legend.position="none",
        axis.text.x = element_text(size = 12),
        axis.text.y = element_text(size = 12),
        axis.title = element_text(size = 14, face = "bold.italic"),
        title = element_text(size = 14, face = "bold.italic"),
        panel.grid.major = element_line(colour = 'grey', linetype = 'dashed'), 
        panel.grid.minor = element_blank(),
        panel.background = element_blank(), 
        axis.line = element_line(colour = "black"))

  1. Calculate Pearson’s Correlation
cor(in.cell.type.connectivity.score$Out.Cell.Type, in.cell.type.connectivity.score$transition.score)
          [,1]
[1,] 0.8421593
LS0tCnRpdGxlOiAiQ2FweWJhcmEgQW5hbHlzaXMgb2YgUGF1bCBldCBhbC4iCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCiMjIyBCcmllZiBEZXNjcmlwdGlvbgpJbiB0aGlzIG5vdGVib29rLCB3ZSBkb2N1bWVudCBDYXB5YmFyYSBhbmFseXNpcyBvZiB0aGUgUGF1bCBldCBhbC4sIDIwMTUgaGVtYXRvcG9pZXNpcyBkYXRhc2V0LCBjaGFydGluZyBkaWZmZXJlbnRpYXRpb24gb2YgYm9uZSBtYXJyb3ctZGVyaXZlZCBteWVsb2lkIHByb2dlbml0b3JzLiBXZSB1c2UgdGhpcyBkYXRhc2V0IHRvIHNob3djYXNlIHRoZSBhcHBsaWNhdGlvbiBvZiBDYXB5YmFyYSB0byBhIHdlbGwtZGVmaW5lZCBkZXZlbG9wbWVudGFsIHByb2Nlc3MuIFdlIGxldmVyYWdlIFBBR0EtYmFzZWQgcHNldWRvdGltZSBpbmZvcm1hdGlvbiB0byBjb21wYXJlIGh5YnJpZCBjZWxscyB0byB0aGVpciBkaXNjcmV0ZSBjb3VudGVycGFydHMsIGRlbW9uc3RyYXRpbmcgdGhlIGJpb2xvZ2ljYWwgcmVsZXZhbmNlIG9mIGh5YnJpZCBjZWxscy4KCkZvciBkZXRhaWxzIG9mIHRoZSBkYXRhc2V0LCBwbGVhc2UgcmVmZXIgdG8gdGhlIFBhdWwgcGFwZXIgaGVyZSAoaHR0cHM6Ly93d3cuc2NpZW5jZWRpcmVjdC5jb20vc2NpZW5jZS9hcnRpY2xlL3BpaS9TMDA5Mjg2NzQxNTAxNDkzMikuIEZvciBkZXRhaWxzIG9mIENhcHliYXJhIGNlbGwtdHlwZSBjbGFzc2lmaWNhdGlvbiwgcGxlYXNlIHJlZmVyIHRvIHRoZSBDYXB5YmFyYSBwYXBlciBoZXJlIChodHRwczovL3d3dy5zY2llbmNlZGlyZWN0LmNvbS9zY2llbmNlL2FydGljbGUvcGlpL1MxOTM0NTkwOTIyMDAwOTk2P2RnY2lkPWNvYXV0aG9yKS4KCiMjIyBMb2FkIHBhY2thZ2VzCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQpsaWJyYXJ5KFNldXJhdCkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KFNldXJhdERpc2spCmxpYnJhcnkoZ2dyZXBlbCkKYGBgCgojIyMgQmFzaWMgSW5mb3JtYXRpb24KQmFzaWMgaW5mb3JtYXRpb24gaXMgb2J0YWluZWQgZnJvbSB0aGUgZm9sbG93aW5nIHR1dG9yaWFsOiBodHRwczovL3NjYW5weS10dXRvcmlhbHMucmVhZHRoZWRvY3MuaW8vZW4vbGF0ZXN0L3BhZ2EtcGF1bDE1Lmh0bWwuIFBsZWFzZSBmaW5kIG1vcmUgaW5mb3JtYXRpb24gaW4gdGhlIEp1cHl0ZXIgbm90ZWJvb2sgc3VwcGxpZWQgdGhlcmUuCgojIyMjIExvYWQgdGhlIGNvdW50IG1hdHJpeApIZXJlIHdlIGxvYWQgdGhlIFBhdWwgZXQgYWwgMjAxNSBjb3VudCBtYXRyaXgsIHNvdXJjZWQgZnJvbSB0aGUgUEFHQSB0dXRvcmlhbC4gCmBgYHtyfQpwYXVsX2NzdiA8LSByZWFkLmNzdigifi9EZXNrdG9wL1JlcHJvZHVjaWJpbGl0eS9GaWd1cmUgMi9JbnRlcm1lZGlhdGVzL0RhdGEvcGF1bF9jb3VudF9tYXR4LmNzdiIsIGNoZWNrLm5hbWVzID0gRiwgc3RyaW5nc0FzRmFjdG9ycyA9IEYsIHJvdy5uYW1lcyA9IDEpCnBhdWxfY3N2X3QgPC0gYXMuZGF0YS5mcmFtZSh0KHBhdWxfY3N2KSkKY29sbmFtZXMocGF1bF9jc3ZfdCkgPC0gcGFzdGUwKCJDZWxsXyIsIGNvbG5hbWVzKHBhdWxfY3N2X3QpKQpgYGAKCiMjIyMgTG9hZCB0aGUgY29vcmRpbmF0ZXMKSGVyZSB3ZSBsb2FkIHRoZSBjb29yZGluYXRlcyBmb3IgdGhlIGZvcmNlIGF0bGFzIGVtYmVkZGluZywgZ3VpZGVkIGJ5IFBBR0EsIGZvciBkb3duc3RyZWFtIHZpc3VhbGl6YXRpb24uIApgYGB7cn0KY29vcmQgPC0gcmVhZC5jc3YoIn4vRGVza3RvcC9SZXByb2R1Y2liaWxpdHkvRmlndXJlIDIvSW50ZXJtZWRpYXRlcy9EYXRhL2Nvb3JkaW5hdGVzLmNzdiIsIGhlYWRlciA9IEYpCnJvd25hbWVzKGNvb3JkKSA8LSBwYXN0ZTAoIkNlbGxfIiwgKHNlcShucm93KGNvb3JkKSkgLSAxKSkKYGBgCgojIyMjIExvYWQgbWV0YSBkYXRhCkhlcmUgd2UgbG9hZCB0aGUgY29vcmRpbmF0ZXMgZm9yIHRoZSBmb3JjZSBhdGxhcyBlbWJlZGRpbmcsIGd1aWRlZCBieSBQQUdBLCBmb3IgZG93bnN0cmVhbSB2aXN1YWxpemF0aW9uLiAKYGBge3J9Cm1ldGEgPC0gcmVhZC5jc3YoIn4vRGVza3RvcC9SZXByb2R1Y2liaWxpdHkvRmlndXJlIDIvSW50ZXJtZWRpYXRlcy9EYXRhL21ldGFfZGF0YS5jc3YiLCBoZWFkZXIgPSBULCByb3cubmFtZXMgPSAxLCBjaGVjay5uYW1lcyA9IEYsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpyb3duYW1lcyhtZXRhKSA8LSBwYXN0ZTAoIkNlbGxfIiwgcm93bmFtZXMobWV0YSkpCmBgYAoKV2UgbWVyZ2UgdGhlIG1ldGEgZGF0YSB3aXRoIHRoZSBjb29yZGluYXRlcwpgYGB7cn0KbWV0YSA8LSBjYmluZChtZXRhLCBjb29yZFtyb3duYW1lcyhtZXRhKSxdKQpgYGAKCiMjIyMgUHNldWRvdGltZSBQcm9qZWN0aW9uCkhlcmUgd2UgaGF2ZSBhIHF1aWNrIGNoZWNrIG9uIHRoZSBwc2V1ZG90aW1lIHByb2plY3Rpb24gd2l0aCB0aGUgcHJvcGVyIGNvb3JkaW5hdGVzIHRvIG1ha2Ugc3VyZSBhbGwgY2VsbHMgYXJlIHByb3Blcmx5IGxvY2F0ZWQgb24gdGhlIEZBIHBsb3QuCmBgYHtyLCBmaWcud2lkdGg9NC41LCBmaWcuaGVpZ2h0PTR9CmdncGxvdChtZXRhLCBhZXMoeCA9IFYxLCB5ID0gVjIsIGNvbG9yID0gZHB0X3BzZXVkb3RpbWUpKSArCiAgZ2VvbV9wb2ludCgpICsKICBzY2FsZV9jb2xvcl92aXJpZGlzX2MobmFtZSA9ICJwc2V1ZG90aW1lIiwgZGlyZWN0aW9uID0gMSwgb3B0aW9uID0gIkEiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJyaWdodCIsCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgIHRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZC5pdGFsaWMiLCBzaXplID0gMTQpLAogICAgICAgIGF4aXMubGluZSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpKQpgYGAKCiMjIyBDYXB5YmFyYQpCcmllZmx5LCB3ZSBhcHBseSBDYXB5YmFyYSBvbiB0aGUgZGF0YXNldCBsb2FkZWQgYWJvdmUuIFdlIGRvIE5PVCBpbmNsdWRlIHRoZSBNQ0EgZGF0YSBvciB0aGUgYnVsayBzZWxlY3Rpb24gc3RlcCBoZXJlIGNvbnNpZGVyaW5nIGl0cyByZWxhdGl2ZWx5IGxhcmdlIHNpemUuIFRoZSBidWxrIHNlbGVjdGlvbiBzdGVwIHByb3ZpZGVkIDQgcmVsZXZhbnQgdGlzc3VlczogYm9uZSBtYXJyb3csIGJvbmUgbWFycm93IGNraXQgKGdyb3VwZWQgYXMgJ2JvbmUgbWFycm93JyBpbiB0aGUgbWFudXNjcmlwdCksIGJvbmUgbWFycm93IG1lc2VuY2h5bWUgKHByaW1hcnkgbWVzZW5jaHltYWwgc3RlbSBjZWxscyksIGFuZCBwZXJpcGhlcmFsIGJsb29kLiBXZSBkbyBpbmNsdWRlIHRoZSBwaXBlbGluZSBmb3IgY29uc3RydWN0aW9uIG9mIHRoZSBoaWdoIHJlc29sdXRpb24gcmVmZXJlbmNlLiBJZiB5b3Ugd291bGQgbGlrZSB0byBydW4gdGhpcyBmcm9tIHRoZSByYXcgTUNBIGRhdGEsIHBsZWFzZSBkb3dubG9hZCB0aGUgZGF0YSBoZXJlIChodHRwOi8vYmlzLnpqdS5lZHUuY24vTUNBL2F0bGFzMi5odG1sKSwgZWRpdCB0aGUgZGlyZWN0b3JpZXMgYW5kIHJlY29uc3RydWN0IHRoZSBoaWdoLXJlc29sdXRpb24gcmVmZXJlbmNlLgoKSW4gZ2VuZXJhbCwgZm9yIHJ1bnRpbWUgY29uc2lkZXJhdGlvbiwgd2UgcHJvY2Vzc2VkIHRoZSBkYXRhc2V0IG9uIGEgSGlnaCBQZXJmb3JtYW5jZSBDb21wdXRpbmcgcmVzb3VyY2UuIEhlbmNlLCB3ZSBpbmNsdWRlIHRoZSBpbnRlcm1lZGlhdGUgZmlsZXMsIHN1Y2ggYXMgdGhlIHJlZmVyZW5jZSwgUVAgb3V0Y29tZXMsIGFuZCBwZXJtdXRhdGlvbiByZXN1bHRzLCBpbiB0aGlzIGZvbGRlciBmb3IgZmFzdGVyIHByb2Nlc3NpbmcuCgojIyMjIExvYWQgcGFja2FnZXMKYGBge3J9CmxpYnJhcnkoQ2FweWJhcmEpCmxpYnJhcnkoTUFTUykKYGBgCgojIyMjIENvbnN0cnVjdCB0aGUgaGlnaCByZXNvbHV0aW9uIHJlZmVyZW5jZSBmcm9tIHRoZSBNb3VzZSBDZWxsIEF0bGFzCjEpIEhlcmUgd2Ugd2lsbCBsb2FkIHRoZSBNQ0EgZGF0YSByZWxhdGVkIHRvIHRoZSA0IHJlbGV2YW50IHRpc3N1ZXMuCmBgYHtyLCBldmFsPUZBTFNFfQojIEJhY2tncm91bmQgY2VsbHMKbWNhIDwtIHJlYWQuY3N2KCJ+L0JveCBTeW5jL01vcnJpcyBMYWIvQ2xhc3NpZmllciBBbmFseXNpcy9SZWZlcmVuY2UgZGF0YXNldHMvTUNBL01DQV9DZWxsQXNzaWdubWVudHMuY3N2IiwKICAgICAgICAgICAgICAgIHJvdy5uYW1lcyA9IDEsIGhlYWRlciA9IFQsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQptY2EubWV0YSA8LSBkYXRhLmZyYW1lKHJvdy5uYW1lcyA9IG1jYSRDZWxsLm5hbWUsIAogICAgICAgICAgICAgICAgICAgICAgIHRpc3N1ZSA9IG1jYSRUaXNzdWUsCiAgICAgICAgICAgICAgICAgICAgICAgY2VsbC5iYy50aXNzdWUgPSB1bmxpc3QobGFwcGx5KHN0cnNwbGl0KG1jYSRDZWxsLm5hbWUsICJfIiksIGZ1bmN0aW9uKHgpIHhbMV0pKSwKICAgICAgICAgICAgICAgICAgICAgICBjZWxsLnR5cGUgPSBtY2EkQW5ub3RhdGlvbiwKICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKCmJvbmUubWFycm93LmNvdW50cyA8LSByZWFkLnRhYmxlKCJ+L0JveCBTeW5jL01vcnJpcyBMYWIvQ2xhc3NpZmllciBBbmFseXNpcy9SZWZlcmVuY2UgZGF0YXNldHMvTUNBL01DQV9Db3VudHNfY3VyYXRlZC9Cb25lLU1hcnJvdy9Cb25lTWFycm93MV9ybS5iYXRjaF9kZ2UudHh0IiwgaGVhZGVyID0gVCwgcm93Lm5hbWVzID0gMSwgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCmJvbmUubWFycm93LmNvdW50cy4yIDwtIHJlYWQudGFibGUoIn4vQm94IFN5bmMvTW9ycmlzIExhYi9DbGFzc2lmaWVyIEFuYWx5c2lzL1JlZmVyZW5jZSBkYXRhc2V0cy9NQ0EvTUNBX0NvdW50c19jdXJhdGVkL0JvbmUtTWFycm93L0JvbmVNYXJyb3cyX3JtLmJhdGNoX2RnZS50eHQiLCBoZWFkZXIgPSBULCByb3cubmFtZXMgPSAxLCBzdHJpbmdzQXNGYWN0b3JzID0gRikKY29sbmFtZXMoYm9uZS5tYXJyb3cuY291bnRzLjIpIDwtIGdzdWIoIl80IiwgIl8yIiwgY29sbmFtZXMoYm9uZS5tYXJyb3cuY291bnRzLjIpKQoKYm9uZS5tYXJyb3cuY291bnRzLjMgPC0gcmVhZC50YWJsZSgifi9Cb3ggU3luYy9Nb3JyaXMgTGFiL0NsYXNzaWZpZXIgQW5hbHlzaXMvUmVmZXJlbmNlIGRhdGFzZXRzL01DQS9NQ0FfQ291bnRzX2N1cmF0ZWQvQm9uZS1NYXJyb3cvQm9uZU1hcnJvdzNfcm0uYmF0Y2hfZGdlLnR4dCIsIGhlYWRlciA9IFQsIHJvdy5uYW1lcyA9IDEsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpjb2xuYW1lcyhib25lLm1hcnJvdy5jb3VudHMuMykgPC0gZ3N1YigiXzUiLCAiXzMiLCBjb2xuYW1lcyhib25lLm1hcnJvdy5jb3VudHMuMykpCgpib25lLm1hcnJvdy5nZW5lcyA8LSBpbnRlcnNlY3QoaW50ZXJzZWN0KHJvd25hbWVzKGJvbmUubWFycm93LmNvdW50cyksIHJvd25hbWVzKGJvbmUubWFycm93LmNvdW50cy4yKSksIHJvd25hbWVzKGJvbmUubWFycm93LmNvdW50cy4zKSkKCmJvbmUubWFycm93LmNvdW50cy5hbGwgPC0gY2JpbmQoY2JpbmQoYm9uZS5tYXJyb3cuY291bnRzW2JvbmUubWFycm93LmdlbmVzLF0sIGJvbmUubWFycm93LmNvdW50cy4yW2JvbmUubWFycm93LmdlbmVzLCBdKSwgYm9uZS5tYXJyb3cuY291bnRzLjNbYm9uZS5tYXJyb3cuZ2VuZXMsXSkKCmJvbmUubWFycm93Lm0gPC0gcmVhZC50YWJsZSgifi9Cb3ggU3luYy9Nb3JyaXMgTGFiL0NsYXNzaWZpZXIgQW5hbHlzaXMvUmVmZXJlbmNlIGRhdGFzZXRzL01DQS9NQ0FfQ291bnRzX2N1cmF0ZWQvQm9uZV9NYXJyb3dfTWVzZW5jaHltZS9NZXNlbmNoeW1hbFN0ZW1DZWxsc1ByaW1hcnlfcm0uYmF0Y2hfZGdlLnR4dCIsIGhlYWRlciA9IFQsIHJvdy5uYW1lcyA9IDEsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQoKYm9uZS5tYXJyb3cuY2tpdCA8LSByZWFkLnRhYmxlKCJ+L0JveCBTeW5jL01vcnJpcyBMYWIvQ2xhc3NpZmllciBBbmFseXNpcy9SZWZlcmVuY2UgZGF0YXNldHMvTUNBL01DQV9Db3VudHNfY3VyYXRlZC9Cb25lLU1hcnJvd19jLWtpdC9Cb25lTWFycm93Y0tpdDFfcm0uYmF0Y2hfZGdlLnR4dCIsIGhlYWRlciA9IFQsIHJvdy5uYW1lcyA9IDEsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpib25lLm1hcnJvdy5ja2l0LjIgPC0gcmVhZC50YWJsZSgifi9Cb3ggU3luYy9Nb3JyaXMgTGFiL0NsYXNzaWZpZXIgQW5hbHlzaXMvUmVmZXJlbmNlIGRhdGFzZXRzL01DQS9NQ0FfQ291bnRzX2N1cmF0ZWQvQm9uZS1NYXJyb3dfYy1raXQvQm9uZU1hcnJvd2NLaXQyX3JtLmJhdGNoX2RnZS50eHQiLCBoZWFkZXIgPSBULCByb3cubmFtZXMgPSAxLCBzdHJpbmdzQXNGYWN0b3JzID0gRikKYm9uZS5tYXJyb3cuY2tpdC4zIDwtIHJlYWQudGFibGUoIn4vQm94IFN5bmMvTW9ycmlzIExhYi9DbGFzc2lmaWVyIEFuYWx5c2lzL1JlZmVyZW5jZSBkYXRhc2V0cy9NQ0EvTUNBX0NvdW50c19jdXJhdGVkL0JvbmUtTWFycm93X2Mta2l0L0JvbmVNYXJyb3djS2l0M19ybS5iYXRjaF9kZ2UudHh0IiwgaGVhZGVyID0gVCwgcm93Lm5hbWVzID0gMSwgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCgpib25lLm1hcnJvdy5ja2l0LmdlbmVzIDwtIGludGVyc2VjdChpbnRlcnNlY3Qocm93bmFtZXMoYm9uZS5tYXJyb3cuY2tpdCksIHJvd25hbWVzKGJvbmUubWFycm93LmNraXQuMikpLCByb3duYW1lcyhib25lLm1hcnJvdy5ja2l0LjMpKQpib25lLm1hcnJvdy5jb3VudHMuY2tpdC5hbGwgPC0gY2JpbmQoY2JpbmQoYm9uZS5tYXJyb3cuY2tpdFtib25lLm1hcnJvdy5ja2l0LmdlbmVzLF0sIGJvbmUubWFycm93LmNraXQuMltib25lLm1hcnJvdy5ja2l0LmdlbmVzLCBdKSwgYm9uZS5tYXJyb3cuY2tpdC4zW2JvbmUubWFycm93LmNraXQuZ2VuZXMsXSkKCnBlcmlwaHJhbC5ibG9vZC4xIDwtIHJlYWQuY3N2KCJ+L0JveCBTeW5jL01vcnJpcyBMYWIvQ2xhc3NpZmllciBBbmFseXNpcy9SZWZlcmVuY2UgZGF0YXNldHMvTUNBL01DQV9Db3VudHNfY3VyYXRlZC9QZXJpcGhlcmFsX0Jsb29kL1BlcmlwaGVyYWxCbG9vZDFfcm0uYmF0Y2hfZGdlLnR4dCIsIHNlcCA9ICJcdCIsIGhlYWRlciA9IFQsIHJvdy5uYW1lcyA9IDEsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpwZXJpcGhyYWwuYmxvb2QuMiA8LSByZWFkLmNzdigifi9Cb3ggU3luYy9Nb3JyaXMgTGFiL0NsYXNzaWZpZXIgQW5hbHlzaXMvUmVmZXJlbmNlIGRhdGFzZXRzL01DQS9NQ0FfQ291bnRzX2N1cmF0ZWQvUGVyaXBoZXJhbF9CbG9vZC9QZXJpcGhlcmFsQmxvb2QyX3JtLmJhdGNoX2RnZS50eHQiLCBzZXAgPSAiXHQiLCBoZWFkZXIgPSBULCByb3cubmFtZXMgPSAxLCBzdHJpbmdzQXNGYWN0b3JzID0gRikKcGVyaXBocmFsLmJsb29kLjMgPC0gcmVhZC5jc3YoIn4vQm94IFN5bmMvTW9ycmlzIExhYi9DbGFzc2lmaWVyIEFuYWx5c2lzL1JlZmVyZW5jZSBkYXRhc2V0cy9NQ0EvTUNBX0NvdW50c19jdXJhdGVkL1BlcmlwaGVyYWxfQmxvb2QvUGVyaXBoZXJhbEJsb29kM19ybS5iYXRjaF9kZ2UudHh0Iiwgc2VwID0gIlx0IiwgaGVhZGVyID0gVCwgcm93Lm5hbWVzID0gMSwgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCnBlcmlwaHJhbC5ibG9vZC40IDwtIHJlYWQuY3N2KCJ+L0JveCBTeW5jL01vcnJpcyBMYWIvQ2xhc3NpZmllciBBbmFseXNpcy9SZWZlcmVuY2UgZGF0YXNldHMvTUNBL01DQV9Db3VudHNfY3VyYXRlZC9QZXJpcGhlcmFsX0Jsb29kL1BlcmlwaGVyYWxCbG9vZDRfcm0uYmF0Y2hfZGdlLnR4dCIsIHNlcCA9ICJcdCIsIGhlYWRlciA9IFQsIHJvdy5uYW1lcyA9IDEsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpwZXJpcGhyYWwuYmxvb2QuNSA8LSByZWFkLmNzdigifi9Cb3ggU3luYy9Nb3JyaXMgTGFiL0NsYXNzaWZpZXIgQW5hbHlzaXMvUmVmZXJlbmNlIGRhdGFzZXRzL01DQS9NQ0FfQ291bnRzX2N1cmF0ZWQvUGVyaXBoZXJhbF9CbG9vZC9QZXJpcGhlcmFsQmxvb2Q1X3JtLmJhdGNoX2RnZS50eHQiLCBzZXAgPSAiXHQiLCBoZWFkZXIgPSBULCByb3cubmFtZXMgPSAxLCBzdHJpbmdzQXNGYWN0b3JzID0gRikKcGVyaXBocmFsLmJsb29kLjYgPC0gcmVhZC5jc3YoIn4vQm94IFN5bmMvTW9ycmlzIExhYi9DbGFzc2lmaWVyIEFuYWx5c2lzL1JlZmVyZW5jZSBkYXRhc2V0cy9NQ0EvTUNBX0NvdW50c19jdXJhdGVkL1BlcmlwaGVyYWxfQmxvb2QvUGVyaXBoZXJhbEJsb29kNl9ybS5iYXRjaF9kZ2UudHh0Iiwgc2VwID0gIlx0IiwgaGVhZGVyID0gVCwgcm93Lm5hbWVzID0gMSwgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCgpwYi5nZW5lcyA8LSBpbnRlcnNlY3QoaW50ZXJzZWN0KGludGVyc2VjdChyb3duYW1lcyhwZXJpcGhyYWwuYmxvb2QuMSksIHJvd25hbWVzKHBlcmlwaHJhbC5ibG9vZC4yKSksCiAgICAgICAgICAgICAgICAgICAgICBpbnRlcnNlY3Qocm93bmFtZXMocGVyaXBocmFsLmJsb29kLjMpLCByb3duYW1lcyhwZXJpcGhyYWwuYmxvb2QuNCkpKSwKICAgICAgICAgICAgICAgICAgICAgIGludGVyc2VjdChyb3duYW1lcyhwZXJpcGhyYWwuYmxvb2QuNSksIHJvd25hbWVzKHBlcmlwaHJhbC5ibG9vZC42KSkpCnBiLmNvdW50cy5hbGwgPC0gY2JpbmQocGVyaXBocmFsLmJsb29kLjFbcGIuZ2VuZXMsIF0sIHBlcmlwaHJhbC5ibG9vZC4yW3BiLmdlbmVzLCBdLCBwZXJpcGhyYWwuYmxvb2QuM1twYi5nZW5lcywgXSwKICAgICAgICAgICAgICAgICAgICAgICBwZXJpcGhyYWwuYmxvb2QuNFtwYi5nZW5lcywgXSwgcGVyaXBocmFsLmJsb29kLjVbcGIuZ2VuZXMsIF0sIHBlcmlwaHJhbC5ibG9vZC42W3BiLmdlbmVzLCBdKQoKYWxsLmJtLnBiLmdlbmVzIDwtIGludGVyc2VjdChpbnRlcnNlY3QoaW50ZXJzZWN0KGJvbmUubWFycm93LmdlbmVzLCBib25lLm1hcnJvdy5ja2l0LmdlbmVzKSwgcm93bmFtZXMoYm9uZS5tYXJyb3cubSkpLCBwYi5nZW5lcykKCmJvbmUubWFycm93LmFsbCA8LSBjYmluZChib25lLm1hcnJvdy5jb3VudHMuYWxsW2FsbC5ibS5wYi5nZW5lcywgXSwgYm9uZS5tYXJyb3cubVthbGwuYm0ucGIuZ2VuZXMsIF0sCiAgICAgICAgICAgICAgICAgICAgICAgICBib25lLm1hcnJvdy5jb3VudHMuY2tpdC5hbGxbYWxsLmJtLnBiLmdlbmVzLCBdLCBwYi5jb3VudHMuYWxsW2FsbC5ibS5wYi5nZW5lcywgXSkKCmJvbmUubWFycm93Lm1ldGEgPC0gbWNhLm1ldGFbd2hpY2gobWNhLm1ldGEkdGlzc3VlICVpbiUgYygiQm9uZS1NYXJyb3ciLCAiQm9uZV9NYXJyb3dfTWVzZW5jaHltZSIsICJCb25lLU1hcnJvd19jLWtpdCIsICJQZXJpcGhlcmFsX0Jsb29kIikpLCBdCmJvbmUubWFycm93Lm1ldGEuMiA8LSBib25lLm1hcnJvdy5tZXRhIyBtY2EubWV0YVt3aGljaChtY2EubWV0YSRjZWxsLnR5cGUgJWluJSBmaW5hbC5jZWxsLnR5cGVzKSxdCmBgYAoKMikgQ2xlYW4gdXAgdGhlIGNlbGwtdHlwZSBsYWJlbHMgdG8gY3JlYXRlIHVuaWZvcm0gbGFiZWxpbmcKYGBge3IsIGV2YWw9RkFMU0V9CmJvbmUubWFycm93Lm1ldGEkY2VsbC50eXBlLjEgPC0gYm9uZS5tYXJyb3cubWV0YSRjZWxsLnR5cGUKYm9uZS5tYXJyb3cubWV0YSRjZWxsLnR5cGUuMSA8LSBnc3ViKCJcXChCb25lLU1hcnJvd1xcKSIsICIiLCBib25lLm1hcnJvdy5tZXRhJGNlbGwudHlwZS4xKQpib25lLm1hcnJvdy5tZXRhJGNlbGwudHlwZS4xIDwtIGdzdWIoIlxcKEJvbmVfTWFycm93X01lc2VuY2h5bWVcXCkiLCAiIiwgYm9uZS5tYXJyb3cubWV0YSRjZWxsLnR5cGUuMSkKYm9uZS5tYXJyb3cubWV0YSRjZWxsLnR5cGUuMSA8LSBnc3ViKCJcXChCb25lLU1hcnJvd19jLWtpdFxcKSIsICIiLCBib25lLm1hcnJvdy5tZXRhJGNlbGwudHlwZS4xKQpib25lLm1hcnJvdy5tZXRhJGNlbGwudHlwZS4xIDwtIGdzdWIoIlxcKFBlcmlwaGVyYWxfQmxvb2RcXCkiLCAiIiwgYm9uZS5tYXJyb3cubWV0YSRjZWxsLnR5cGUuMSkKCmJvbmUubWFycm93Lm1ldGEuMiRjZWxsLnR5cGUuMSA8LSBib25lLm1hcnJvdy5tZXRhLjIkY2VsbC50eXBlCmJvbmUubWFycm93Lm1ldGEuMiRjZWxsLnR5cGUuMSA8LSBnc3ViKCJcXChCb25lLU1hcnJvd1xcKSIsICIiLCBib25lLm1hcnJvdy5tZXRhLjIkY2VsbC50eXBlLjEpCmJvbmUubWFycm93Lm1ldGEuMiRjZWxsLnR5cGUuMSA8LSBnc3ViKCJcXChCb25lX01hcnJvd19NZXNlbmNoeW1lXFwpIiwgIiIsIGJvbmUubWFycm93Lm1ldGEuMiRjZWxsLnR5cGUuMSkKYm9uZS5tYXJyb3cubWV0YS4yJGNlbGwudHlwZS4xIDwtIGdzdWIoIlxcKEJvbmUtTWFycm93X2Mta2l0XFwpIiwgIiIsIGJvbmUubWFycm93Lm1ldGEuMiRjZWxsLnR5cGUuMSkKYm9uZS5tYXJyb3cubWV0YS4yJGNlbGwudHlwZS4xIDwtIGdzdWIoIlxcKFBlcmlwaGVyYWxfQmxvb2RcXCkiLCAiIiwgYm9uZS5tYXJyb3cubWV0YS4yJGNlbGwudHlwZS4xKQpib25lLm1hcnJvdy5tZXRhLjIkY2VsbC50eXBlLjFbd2hpY2goYm9uZS5tYXJyb3cubWV0YS4yJGNlbGwudHlwZS4xID09ICJOZXV0cm9waGlsICIpXSA8LSAiTmV1dHJvcGhpbCIKCmJvbmUubWFycm93Lm1ldGEkY2VsbC50eXBlLjIgPC0gdW5saXN0KGxhcHBseShzdHJzcGxpdChib25lLm1hcnJvdy5tZXRhJGNlbGwudHlwZS4xLCAiXyIpLCBmdW5jdGlvbih4KSB4WzFdKSkKYm9uZS5tYXJyb3cubWV0YSRjZWxsLnR5cGUuMlt3aGljaChib25lLm1hcnJvdy5tZXRhJGNlbGwudHlwZS4yID09ICJOZXV0cm9waGlsICIpXSA8LSAiTmV1dHJvcGhpbCIKYm9uZS5tYXJyb3cubWV0YSRjZWxsLnR5cGUuMlt3aGljaChib25lLm1hcnJvdy5tZXRhJGNlbGwudHlwZS4yID09ICJNb25vY3l0ZSBwcm9nZW5pdG9yIGNlbGwiKV0gPC0gIk1vbm9jeXRlIHByb2dlbml0b3IiCmJvbmUubWFycm93Lm1ldGEkY2VsbC50eXBlLjJbd2hpY2goYm9uZS5tYXJyb3cubWV0YSRjZWxsLnR5cGUuMiA9PSAiQmFzb3BoaWxzIildIDwtICJCYXNvcGhpbCIKYGBgCgozKSBEb3duc2FtcGxlIGNlbGxzIGluIGVhY2ggdGlzc3VlIGJ5IHNhbXBsaW5nIGFuZCBjb25zdHJ1Y3QgdGhlIHJlZmVyZW5jZSBmcm9tIGhlcmUuCmBgYHtyLCBldmFsPUZBTFNFfQpibSA8LSByb3duYW1lcyhib25lLm1hcnJvdy5tZXRhLjIpW3doaWNoKGJvbmUubWFycm93Lm1ldGEuMiRjZWxsLmJjLnRpc3N1ZSA9PSAiQm9uZU1hcnJvdyIpXQpibWNrIDwtIHJvd25hbWVzKGJvbmUubWFycm93Lm1ldGEuMilbd2hpY2goYm9uZS5tYXJyb3cubWV0YS4yJGNlbGwuYmMudGlzc3VlID09ICJCb25lTWFycm93Y0tpdCIpXQptc2NwIDwtIHJvd25hbWVzKGJvbmUubWFycm93Lm1ldGEuMilbd2hpY2goYm9uZS5tYXJyb3cubWV0YS4yJGNlbGwuYmMudGlzc3VlID09ICJNZXNlbmNoeW1hbFN0ZW1DZWxsc1ByaW1hcnkiKV0KcGIgPC0gcm93bmFtZXMoYm9uZS5tYXJyb3cubWV0YS4yKVt3aGljaChib25lLm1hcnJvdy5tZXRhLjIkY2VsbC5iYy50aXNzdWUgPT0gIlBlcmlwaGVyYWxCbG9vZCIpXQoKc2FtcGxlX2xpc3QgPC0gYyhzYW1wbGUoYm0sIDI1MDApLAogICAgICAgICAgICAgICAgIHNhbXBsZShibWNrLCAyNTAwKSwKICAgICAgICAgICAgICAgICBzYW1wbGUobXNjcCwgMjUwMCksCiAgICAgICAgICAgICAgICAgc2FtcGxlKHBiLCAyNTAwKSkKYGBgCgo0KSBUaGUgY2VsbHMgYXJlIHVzZWQgdG8gY29uc3RydWN0IHRoZSByZWZlcmVuY2UKYGBge3IsIGV2YWw9RkFMU0V9Cm1ldGEuc3ViIDwtIGJvbmUubWFycm93Lm1ldGFbc2FtcGxlX2xpc3QsIF0KcmVmZXJlbmNlLnJzbHQgPC0gY29uc3RydWN0LmhpZ2gucmVzLnJlZmVyZW5jZShib25lLm1hcnJvdy5hbGwsIG1ldGEuc3ViLCBjcml0ZXJpYSA9ICJjZWxsLnR5cGUuMiIsIGNlbGwubnVtLmZvci5yZWYgPSA5MCkKc2F2ZVJEUyhyZWZlcmVuY2UucnNsdCwgIn4vRGVza3RvcC9yZWZlcmVuY2VfcGF1bF8xNS5SZHMiKQpgYGAKCiMjIyMgTG9hZCBwcmUtZ2VuZXJhdGVkIHJlZmVyZW5jZQpIZXJlIHdlIGxvYWQgdGhlIHByZS1nZW5lcmF0ZWQgcmVmZXJlbmNlIGZvciB0aGlzIGFuYWx5c2lzLgpgYGB7cn0KcmVmZXJlbmNlLnJzbHQgPC0gcmVhZFJEUygifi9EZXNrdG9wL1JlcHJvZHVjaWJpbGl0eS9GaWd1cmUgMi9JbnRlcm1lZGlhdGVzL1JlZmVyZW5jZS9yZWZlcmVuY2VfcGF1bF8xNS5SZHMiKQpyZWYuZGYgPC0gcmVmZXJlbmNlLnJzbHRbWzNdXQpyZWYubWV0YSA8LSByZWZlcmVuY2UucnNsdFtbMl1dCnJlZi5zYyA8LSByZWZlcmVuY2UucnNsdFtbMV1dCmBgYAoKIyMjIyBRdWFkcmF0aWMgUHJvZ3JhbW1pbmcKMSkgUnVuIFF1YWRyYXRpYyBQcm9ncmFtbWluZy4KYGBge3IsIGV2YWw9RkFMU0V9CiMgTWVhc3VyZSBjZWxsIGlkZW50aXR5IGluIHRoZSByZWZlcmVuY2UgZGF0YXNldCBhcyBhIGJhY2tncm91bmQgCnNpbmdsZS5yb3VuZC5RUC5hbmFseXNpcyhyZWYuZGYsIHJlZi5zYywgbi5jb3JlcyA9IDQsIHNhdmUudG8ucGF0aCA9ICJ+L0Rlc2t0b3AvIiwgc2F2ZS50by5maWxlbmFtZSA9ICIwMV9NQ0FfQmFzZWRfcmVmZXJlbmNlX3FwX3BhdWxfMTUiLCB1bml4LnBhciA9IFRSVUUpCgojIE1lYXN1cmUgY2VsbCBpZGVudGl0eSBpbiB0aGUgcXVlcnkgZGF0YXNldCAKc2luZ2xlLnJvdW5kLlFQLmFuYWx5c2lzKHJlZi5kZiwgcGF1bF9jc3ZfdCwgbi5jb3JlcyA9IDQsIHNhdmUudG8ucGF0aCA9ICJ+L0Rlc2t0b3AvIiwgc2F2ZS50by5maWxlbmFtZSA9ICIwMV9NQ0FfQmFzZWRfdGVzdF9xcF9wYXVsXzE1IiwgdW5peC5wYXIgPSBUUlVFLCBmb3JjZS5lcSA9IDApCmBgYAoKMikgTG9hZCB0aGUgcHJlLWNhbGN1bGF0ZWQgUVAgcmVzdWx0cyBmb3IgdGhpcyBhbmFseXNpcy4KYGBge3J9CiMgUmVhZCBpbiBiYWNrZ3JvdW5kIGFuZCB0ZXN0aW5nIGlkZW50aXR5IHNjb3JlcwpiYWNrZ3JvdW5kLm10eCA8LSByZWFkLmNzdigifi9EZXNrdG9wL1JlcHJvZHVjaWJpbGl0eS9GaWd1cmUgMi9JbnRlcm1lZGlhdGVzL1FQX091dGNvbWVzLzAxX01DQV9CYXNlZF9yZWZlcmVuY2VfcXBfcGF1bF8xNV9zY2FsZS5jc3YiLCBoZWFkZXIgPSBULCByb3cubmFtZXMgPSAxLCBzdHJpbmdzQXNGYWN0b3JzID0gRikKbXR4LnRlc3QgPC0gcmVhZC5jc3YoIn4vRGVza3RvcC9SZXByb2R1Y2liaWxpdHkvRmlndXJlIDIvSW50ZXJtZWRpYXRlcy9RUF9PdXRjb21lcy8wMV9NQ0FfQmFzZWRfdGVzdF9xcF9wYXVsXzE1X3NjYWxlLmNzdiIsIGhlYWRlciA9IFQsIHJvdy5uYW1lcyA9IDEsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQoKY29sLnN1YiA8LSBuY29sKGJhY2tncm91bmQubXR4KSAtIDIKYGBgCgojIyMjIEVtcGlyaWNhbCBwLXZhbHVlIGNhbGN1bGF0aW9uClRvIGNhbGN1bGF0ZSB0aGUgZW1waXJpY2FsIHAtdmFsdWUsIHJ1biB0aGUgZm9sbG93aW5nIHR3byBsaW5lcy4gSGVyZSB3ZSBza2lwIHRoZXNlIGxpbmVzIHRvIGxvYWQgcHJldmlvdXNseSBvYnRhaW5lZCByZXN1bHRzLgpgYGB7ciwgZXZhbD1GQUxTRX0KIyBDb25kdWN0IHJlZmVyZW5jZSByYW5kb21pemF0aW9uIHRvIGdldCBlbXBpcmljYWwgcC12YWx1ZSBtYXRyaXgKcmVmLnBlcmMubGlzdCA8LSBwZXJjZW50YWdlLmNhbGMoYmFja2dyb3VuZC5tdHhbLGMoMTpjb2wuc3ViKV0sIGJhY2tncm91bmQubXR4WyxjKDE6Y29sLnN1YildKQoKIyBDb25kdWN0IHRlc3QgcmFuZG9taXphdGlvbiB0byBnZXQgZW1waXJpY2FsIHAtdmFsdWUgbWF0cml4CnBlcmMubGlzdCA8LSBwZXJjZW50YWdlLmNhbGMoYXMubWF0cml4KG10eC50ZXN0WyxjKDE6Y29sLnN1YildKSwgYXMubWF0cml4KGJhY2tncm91bmQubXR4WyxjKDE6Y29sLnN1YildKSkKYGBgCgpMb2FkIHRoZSBwcmV2aW91cyBlbXBpcmljYWwgcC12YWx1ZSBkYXRhLgpgYGB7cn0KIyBDb25kdWN0IHJlZmVyZW5jZSByYW5kb21pemF0aW9uIHRvIGdldCBlbXBpcmljYWwgcC12YWx1ZSBtYXRyaXgKcmVmLnBlcmMubGlzdC5hbGwgPC0gcmVhZFJEUygifi9EZXNrdG9wL1JlcHJvZHVjaWJpbGl0eS9GaWd1cmUgMi9JbnRlcm1lZGlhdGVzL1Blcm11dGF0aW9uX1Jlc3VsdHMvcGF1bF8xNV9wZXJtdXRhdGlvbl9yc2x0LlJkcyIpCnJlZi5wZXJjLmxpc3QgPC0gcmVmLnBlcmMubGlzdC5hbGwkcmVmCnBlcmMubGlzdCA8LSByZWYucGVyYy5saXN0LmFsbCRzYW1wbGUKYGBgCgojIyMjIEluaXRpYWwgQ2xhc3NpZmljYXRpb24KV2UgcGVyZm9ybSBpbml0aWFsIGNsYXNzaWZpY2F0aW9uIGJhc2VkIG9uIHF1YWRyYXRpYyBwcm9ncmFtbWluZyBtZXRyaWNzOiBkZXZpYW5jZSwgZXJyb3IsIGFuZCBsYWdyYW5naWFuIG11bHRpcGxpZXJzLiBIZXJlLCB3ZSBwcmltYXJpbHkgbGV2ZXJhZ2UgZGV2aWFuY2UgdG8gZGlzdGluZ3Vpc2ggdW5rbm93biwgZGlzY3JldGUsIGFuZCBoeWJyaWQgY2VsbHMuCgoxKSBDb25zdHJ1Y3QgdGhlIGlkZWFsIGRldmlhbmNlIGRpc3RyaWJ1dGlvbiBiYXNlZCBvbiB0aGUgYmFja2dyb3VuZCBtYXRyaXgKYGBge3J9CmJhY2tncm91bmQubXR4LnNjYWxlIDwtIGFzLmRhdGEuZnJhbWUodChhcHBseShiYWNrZ3JvdW5kLm10eFssYygxOjY3KV0sIDEsIGZ1bmN0aW9uKHgpIHgqKDEvc3VtKHgpKSkpKQppZGVhbC5kZXZpYW5jZSA8LSBhYnMoYmFja2dyb3VuZC5tdHguc2NhbGVbLGMoMTo2NyldIC0gMS82NykKaWRlYWwuZGV2aWFuY2UuYWxsIDwtIHJvd1N1bXMoYWJzKGlkZWFsLmRldmlhbmNlKSkKaWRlYWwuZGV2aWFuY2UuYWxsLm1lYW4gPC0gbWVhbihpZGVhbC5kZXZpYW5jZS5hbGwpCmlkZWFsLmRldmlhbmNlLnNkIDwtIHNkKGlkZWFsLmRldmlhbmNlLmFsbCkKCmZpdCA8LSBmaXRkaXN0cihpZGVhbC5kZXZpYW5jZS5hbGwsIGRlbnNmdW4gPSAibm9ybWFsIikKCmZvcmNlLnRlc3QucXAuZGV2aWFuY2UgPC0gYWJzKG10eC50ZXN0WyxjKDE6NjcpXSAtIDEvNjcpCgpmb3JjZS50ZXN0LnFwLmRldmlhbmNlJHRvdGFsLmRldmlhbmNlIDwtIHJvd1N1bXMoZm9yY2UudGVzdC5xcC5kZXZpYW5jZSkKbXR4LnRlc3QkZGV2aWFuY2UgPC0gZm9yY2UudGVzdC5xcC5kZXZpYW5jZVtyb3duYW1lcyhtdHgudGVzdCksICJ0b3RhbC5kZXZpYW5jZSJdCgppZGVhbC5kZXZpYW5jZS5hbGwubWVhbi5zYyA8LSBpZGVhbC5kZXZpYW5jZS5hbGwubWVhbgoKZ3Vlc3NlZC5tdWx0aS5pZC5kZXZpYW5jZS5tZWFuIDwtIGlkZWFsLmRldmlhbmNlLmFsbC5tZWFuLnNjIC0gaWRlYWwuZGV2aWFuY2Uuc2QgKiAyCmd1ZXNzZWQudW5rbm93bi5kZXZpYW5jZS5tZWFuIDwtIGd1ZXNzZWQubXVsdGkuaWQuZGV2aWFuY2UubWVhbiAtIGlkZWFsLmRldmlhbmNlLnNkICogMgoKbXR4LnRlc3QkZGV2aWFuY2UucCA8LSBwbm9ybShtdHgudGVzdCRkZXZpYW5jZSwgbWVhbiA9IGlkZWFsLmRldmlhbmNlLmFsbC5tZWFuLnNjLCBzZCA9IGlkZWFsLmRldmlhbmNlLnNkLCBsb3dlci50YWlsID0gVCkKbXR4LnRlc3QkZGV2aWFuY2UucC5tdWx0aSA8LSBwbm9ybShtdHgudGVzdCRkZXZpYW5jZSwgbWVhbiA9IGd1ZXNzZWQubXVsdGkuaWQuZGV2aWFuY2UubWVhbiwgc2QgPSBpZGVhbC5kZXZpYW5jZS5zZCAqMiwgbG93ZXIudGFpbCA9IFQpCm10eC50ZXN0JGRldmlhbmNlLnAudW5rbm93biA8LSBwbm9ybShtdHgudGVzdCRkZXZpYW5jZSwgbWVhbiA9IGd1ZXNzZWQudW5rbm93bi5kZXZpYW5jZS5tZWFuLCBzZCA9IGlkZWFsLmRldmlhbmNlLnNkLzIsIGxvd2VyLnRhaWwgPSBUKQpgYGAKCjIpIFRocmVzaG9sZCBzZWxlY3Rpb24KYGBge3J9CnBsb3QobXR4LnRlc3QkZGV2aWFuY2UucC5tdWx0aSwgbXR4LnRlc3QkZGV2aWFuY2UucCkKYWJsaW5lKHYgPSAwLjQsIGNvbCA9ICJyZWQiKQphYmxpbmUoaCA9IDAuMDEsIGNvbCA9ICJibHVlIikKCnBsb3QobXR4LnRlc3QkZGV2aWFuY2UucC51bmtub3duLCBtdHgudGVzdCRkZXZpYW5jZS5wLm11bHRpKQphYmxpbmUodiA9IDAuMDEsIGNvbCA9ICJyZWQiKQphYmxpbmUoaCA9IDAuMDEsIGNvbCA9ICJibHVlIikKYGBgCgozKSBDcmVhdGUgdGhlIGluaXRpYWwgY2xhc3NpZmljYXRpb24gZGF0YSBmcmFtZQpgYGB7cn0KaW5pdC5jbGFzcyA8LSBkYXRhLmZyYW1lKGNlbGwuYmMgPSByb3duYW1lcyhtdHgudGVzdCksIGluaXQuY2xhc3MgPSAiVW5rbm93biIsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpyb3duYW1lcyhpbml0LmNsYXNzKSA8LSBpbml0LmNsYXNzJGNlbGwuYmMKaW5pdC5jbGFzcyRpbml0LmNsYXNzIDwtICJTaW5nbGUtSUQiCmluaXQuY2xhc3Nbcm93bmFtZXMobXR4LnRlc3Rbd2hpY2gobXR4LnRlc3QkZGV2aWFuY2UucCA+PSAwLjAxICYgbXR4LnRlc3QkZGV2aWFuY2UucC5tdWx0aSA+PSAwLjkpLCBdKSwgImluaXQuY2xhc3MiXSA8LSAiU2luZ2xlLUlEIgppbml0LmNsYXNzW3Jvd25hbWVzKG10eC50ZXN0W3doaWNoKG10eC50ZXN0JGRldmlhbmNlLnAubXVsdGkgPj0gMC40ICYgbXR4LnRlc3QkZGV2aWFuY2UucC51bmtub3duID49IDAuOTUgJiBtdHgudGVzdCRkZXZpYW5jZS5wIDwgMC4xKSwgXSksICJpbml0LmNsYXNzIl0gPC0gIk11bHRpLUlEIgppbml0LmNsYXNzW3Jvd25hbWVzKG10eC50ZXN0W3doaWNoKG10eC50ZXN0JGRldmlhbmNlLnAubXVsdGkgPCAwLjAxICYgbXR4LnRlc3QkZGV2aWFuY2UucC51bmtub3duID49IDAuMDEpLCBdKSwgImluaXQuY2xhc3MiXSA8LSAiVW5rbm93biIKYGBgCgo0KSBBc3Nlc3MgdGhlIGluaXRpYWwgY2xhc3NpZmljYXRpb24gYnJlYWtkb3ducwpgYGB7ciwgZmlnLndpZHRoPTQsIGZpZy5oZWlnaHQ9OH0KZnJlcS50YWJsZSA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKGluaXQuY2xhc3MkaW5pdC5jbGFzcykgKiAxMDAvc3VtKHRhYmxlKGluaXQuY2xhc3MkaW5pdC5jbGFzcykpKQpmcmVxLnRhYmxlIDwtIGZyZXEudGFibGVbb3JkZXIoZnJlcS50YWJsZSRGcmVxLCBkZWNyZWFzaW5nID0gVCksIF0KZnJlcS50YWJsZSRWYXIxIDwtIGZhY3Rvcihhcy5jaGFyYWN0ZXIoZnJlcS50YWJsZSRWYXIxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBhcy5jaGFyYWN0ZXIoZnJlcS50YWJsZSRWYXIxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBvcmRlcmVkID0gVCkKCmdncGxvdChmcmVxLnRhYmxlLCBhZXMoeCA9ICJIZW1hdG9wb2llc2lzIiwgeSA9IEZyZXEsIGZpbGwgPSBWYXIxKSkgKwogIGdlb21fYmFyKHBvc2l0aW9uID0gInN0YWNrIiwgc3RhdCA9ICJpZGVudGl0eSIpICsKICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlBhaXJlZCIpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiLAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsIHNpemUgPSAxMiksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwgc2l6ZSA9IDEyKSwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZC5pdGFsaWMiLCBzaXplID0gMTQpLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgdGl0bGUgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkLml0YWxpYyIsIHNpemUgPSAxNCksCiAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIpLAogICAgICAgIGF4aXMudGlja3MueCA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCgojIyMjIEJpbmFyaXphdGlvbiBhbmQgQ2xhc3NpZmljYXRpb24KV2UgZ2VuZXJhdGUgdGhlIGJpbmFyaXphdGlvbiBtYXRyaXggc28gdGhhdCB1bmtub3duIGNlbGxzIGFyZSBsYWJlbGxlZCAwLCB1bmtub3duIHByb2dlbml0b3JzIC0xLCBhbmQga25vd24gY2VsbCB0eXBlcyBsYWJlbGxlZCAxLCBhbmQgcGVyZm9ybSBjbGFzc2lmaWNhdGlvbiBiYXNlZCBvbiB0aGUgYmluYXJpemVkIGNvdW50LgpgYGB7cn0KIyBCaW5hcml6YXRpb24gb2YgaW5mZXJlbmNlIHJlc3VsdHMKYmluLmNvdW50IDwtIGJpbmFyaXphdGlvbi5tYW5uLndoaXRuZXkobXR4ID0gbXR4LnRlc3RbLGMoMTpjb2wuc3ViKV0sIHJlZi5wZXJjLmxzID0gcmVmLnBlcmMubGlzdCwgcmVmLm1ldGEgPSByZWYubWV0YSwgcGVyYy5scyA9IHBlcmMubGlzdCwgaW5pdC5jbGFzcyA9IGluaXQuY2xhc3MpCiMgQ2xhc3NpZmljYXRpb25uCmNsYXNzaWZpY2F0aW9uIDwtIGJpbmFyeS50by5jbGFzc2lmaWNhdGlvbihiaW4uY291bnRbLGMoMTpjb2wuc3ViKV0pCnJvd25hbWVzKGNsYXNzaWZpY2F0aW9uKSA8LSBjbGFzc2lmaWNhdGlvbiRiYXJjb2RlCmBgYAoKQXQgdGhpcyBzdGFnZSwgd2UgaGF2ZSBjb21wbGV0ZWQgY2xhc3NpZmljYXRpb24gb2YgdGhlIGNlbGxzIGluIHRoaXMgZGF0YXNldC4gTmV4dCwgd2Ugd2lsbCB0YWtlIGEgY2xvc2VyIGxvb2sgYXQgdGhlIENhcHliYXJhIGNsYXNzaWZpY2F0aW9uIG91dGNvbWVzIGluIGNvbXBhcmlzb24gdG8gdGhlIGFubm90YXRpb24gZnJvbSBQYXVsIGV0IGFsLiBhbmQgUEFHQS4gCgojIyMgVGlzc3VlcyBNYXBwZWQKMSkgV2Ugb2JzZXJ2ZSB3aGVyZSBtb3N0IG9mIHRoZSBjZWxscyBtYXBwZWQgdG8gYWZ0ZXIgaGlnaC1yZXNvbHV0aW9uIGNsYXNzaWZpY2F0aW9uLiBUaGlzIGluZm9ybWF0aW9uIGlzIGdhdGhlcmVkIGJhc2VkIG9uIHRoZSByZWZlcmVuY2UgbWV0YSBkYXRhLgpgYGB7cn0KcmVmLm1ldGEkdGlzc3VlIDwtIHVubGlzdChsYXBwbHkoc3Ryc3BsaXQocmVmLm1ldGEkY2VsbC5iYywgIl8iKSwgZnVuY3Rpb24oeCkgeFsxXSkpCnJzbHQgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShyZWYubWV0YSRjZWxsLnR5cGUsIHJlZi5tZXRhJHRpc3N1ZSkpCmN0LnRpc3N1ZS5tYXAgPC0gdW5pcXVlKHJlZi5tZXRhWyxjKDEsMyldKQoKY3QudGlzc3VlLm1hcC5kZiA8LSBkYXRhLmZyYW1lKCkKdW5pcS5jdCA8LSB1bmlxdWUoY3QudGlzc3VlLm1hcCRjZWxsLnR5cGUpCgpmb3IgKHVjIGluIHVuaXEuY3QpIHsKICBjdXJyLmN0LnRpc3N1ZS5tYXAgPC0gY3QudGlzc3VlLm1hcFt3aGljaChjdC50aXNzdWUubWFwJGNlbGwudHlwZSA9PSB1YyksIF0KICBpZiAobnJvdyhjdXJyLmN0LnRpc3N1ZS5tYXApIDw9IDEpIHsKICAgIGN1cnIuZGYgPC0gY3Vyci5jdC50aXNzdWUubWFwCiAgfSBlbHNlIHsKICAgIGZyZXEuc3ViIDwtIHJzbHRbd2hpY2goYXMuY2hhcmFjdGVyKHJzbHQkVmFyMSkgPT0gdWMpLCBdCiAgICBtYWpvci5jb250IDwtIGFzLmNoYXJhY3RlcihmcmVxLnN1YiRWYXIyW3doaWNoKGZyZXEuc3ViJEZyZXEgPT0gbWF4KGZyZXEuc3ViJEZyZXEpKV0pCiAgICBjdXJyLmRmIDwtIGRhdGEuZnJhbWUoY2VsbC50eXBlID0gdWMsIHRpc3N1ZSA9IG1ham9yLmNvbnQsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQogIH0KICAKICBpZiAobnJvdyhjdC50aXNzdWUubWFwLmRmKSA8PSAwKSB7CiAgICBjdC50aXNzdWUubWFwLmRmIDwtIGN1cnIuZGYKICB9IGVsc2UgewogICAgY3QudGlzc3VlLm1hcC5kZiA8LSByYmluZChjdC50aXNzdWUubWFwLmRmLCBjdXJyLmRmKQogIH0KfQoKcm93bmFtZXMoY3QudGlzc3VlLm1hcC5kZikgPC0gZ3N1YigiICIsICIuIiwgY3QudGlzc3VlLm1hcC5kZiRjZWxsLnR5cGUpCnJvd25hbWVzKGN0LnRpc3N1ZS5tYXAuZGYpIDwtIGdzdWIoIi0iLCAiLiIsIHJvd25hbWVzKGN0LnRpc3N1ZS5tYXAuZGYpKQoKYGBgCgoyKSBXZSBwbG90IHRoZSBtYXBwZWQgdGlzc3VlIHBlcmNlbnRhZ2VzIHVzaW5nIGEgc3RhY2tlZCBiYXJjaGFydC4gVGhlIGNvbG9yIHNjaGVtZSBvZiB0aGlzIGRpZmZlcnMgZnJvbSB0aGUgcGFwZXIgYXMgd2UgbWVyZ2VkIHRoZSBzdWJjYXRlZ29yaWVzIHVuZGVyIGJvbmUgbWFycm93LgpgYGB7ciwgZmlnLndpZHRoPTUsIGZpZy5oZWlnaHQ9OH0KY2xhc3NpZmljYXRpb24kdGlzc3VlIDwtIGN0LnRpc3N1ZS5tYXAuZGZbY2xhc3NpZmljYXRpb24kY2FsbCwgInRpc3N1ZSJdCgpmcmVxLnRhYmxlIDwtIGFzLmRhdGEuZnJhbWUodGFibGUoY2xhc3NpZmljYXRpb24kdGlzc3VlKSAqIDEwMC9zdW0odGFibGUoY2xhc3NpZmljYXRpb24kdGlzc3VlKSkpCmZyZXEudGFibGUgPC0gZnJlcS50YWJsZVtvcmRlcihmcmVxLnRhYmxlJEZyZXEsIGRlY3JlYXNpbmcgPSBUKSwgXQpmcmVxLnRhYmxlJFZhcjEgPC0gZmFjdG9yKGFzLmNoYXJhY3RlcihmcmVxLnRhYmxlJFZhcjEpLAogICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGFzLmNoYXJhY3RlcihmcmVxLnRhYmxlJFZhcjEpLAogICAgICAgICAgICAgICAgICAgICAgICAgIG9yZGVyZWQgPSBUKQoKZnJlcS50YWJsZSRjb2xvciA8LSBjKFJDb2xvckJyZXdlcjo6YnJld2VyLnBhbCgxMiwgIlBhaXJlZCIpWzNdLAogICAgICAgICAgICAgICAgICAgICAgUkNvbG9yQnJld2VyOjpicmV3ZXIucGFsKDEyLCAiUGFpcmVkIilbN10sCiAgICAgICAgICAgICAgICAgICAgICBSQ29sb3JCcmV3ZXI6OmJyZXdlci5wYWwoMTIsICJQYWlyZWQiKVsyXSwKICAgICAgICAgICAgICAgICAgICAgIFJDb2xvckJyZXdlcjo6YnJld2VyLnBhbCgxMiwgIlBhaXJlZCIpWzZdKQoKZ2dwbG90KGZyZXEudGFibGUsIGFlcyh4ID0gIkhlbWF0b3BvaWVzaXMiLCB5ID0gRnJlcSwgZmlsbCA9IFZhcjEpKSArCiAgZ2VvbV9iYXIocG9zaXRpb24gPSAic3RhY2siLCBzdGF0ID0gImlkZW50aXR5IikgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGZyZXEudGFibGUkY29sb3IpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiLAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsIHNpemUgPSAxMiksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwgc2l6ZSA9IDEyKSwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZC5pdGFsaWMiLCBzaXplID0gMTQpLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgdGl0bGUgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkLml0YWxpYyIsIHNpemUgPSAxNCksCiAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIpLAogICAgICAgIGF4aXMudGlja3MueCA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCk92ZXJhbGwsIHdlIGFyZSBwcmltYXJpbHkgbWFwcGluZyB0byBib25lIG1hcnJvdyAoQm9uZU1hcnJvd2NLaXQgYW5kIEJvbmVNYXJyb3cgYXJlIGFnZ3JlZ2F0ZWQgaW4gdGhlIG1hbnVzY3JpcHQgZmlndXJlLCBmb3Igc2ltcGxpY2l0eSkgYW5kIHNvbWUgcGVyaXBoZXJhbCBibG9vZCBjZWxscy4gV2UgbmV4dCBncm91cCBjZWxscyBhbmQgYW5ub3RhdGUgY2x1c3RlcnMsIGNvbXBhcmluZyB3aXRoIFBhdWwgZXQgYWwuIGFuZCBQQUdBIGFubm90YXRpb25zLgoKIyMjIE1ldGFkYXRhIGNsZWFuIHVwIGFuZCBncm91cGluZwoxKSBHcm91cCBQYXVsICYgUEFHQSBhbm5vdGF0aW9uIGZvciBhIGZpbmFsICJncm91bmQtdHJ1dGgiIGFubm90YXRpb24KYGBge3J9Cm1ldGEuYWxsIDwtIGNiaW5kKG1ldGEsIGNsYXNzaWZpY2F0aW9uW3Jvd25hbWVzKG1ldGEpLCBdKQptZXRhLmFsbCRwYXVsX2Fubm8gPC0gbWV0YS5hbGwkcGF1bDE1X2NsdXN0ZXJzCm1ldGEuYWxsJHBhdWxfYW5ub1t3aGljaChlbmRzV2l0aChtZXRhLmFsbCRwYXVsX2Fubm8sICJNRVAiKSldIDwtICJNRVAiCm1ldGEuYWxsJHBhdWxfYW5ub1t3aGljaChlbmRzV2l0aChtZXRhLmFsbCRwYXVsX2Fubm8sICJHTVAiKSldIDwtICJHTVAiCm1ldGEuYWxsJHBhdWxfYW5ub1t3aGljaChlbmRzV2l0aChtZXRhLmFsbCRwYXVsX2Fubm8sICJEQyIpKV0gPC0gIkRDIgptZXRhLmFsbCRwYXVsX2Fubm9bd2hpY2goZW5kc1dpdGgobWV0YS5hbGwkcGF1bF9hbm5vLCAiQmFzbyIpKV0gPC0gIkJhc28iCm1ldGEuYWxsJHBhdWxfYW5ub1t3aGljaChlbmRzV2l0aChtZXRhLmFsbCRwYXVsX2Fubm8sICJNbyIpKV0gPC0gIk1vIgptZXRhLmFsbCRwYXVsX2Fubm9bd2hpY2goZW5kc1dpdGgobWV0YS5hbGwkcGF1bF9hbm5vLCAiTmV1IikpXSA8LSAiTmV1IgptZXRhLmFsbCRwYXVsX2Fubm9bd2hpY2goZW5kc1dpdGgobWV0YS5hbGwkcGF1bF9hbm5vLCAiRW9zIikpXSA8LSAiRW9zIgptZXRhLmFsbCRwYXVsX2Fubm9bd2hpY2goZW5kc1dpdGgobWV0YS5hbGwkcGF1bF9hbm5vLCAiTHltcGgiKSldIDwtICJMeW1waCIKbWV0YS5hbGwkcGF1bF9hbm5vW3doaWNoKGVuZHNXaXRoKG1ldGEuYWxsJHBhdWxfYW5ubywgIkVyeSIpKV0gPC0gIkVyeSIKbWV0YS5hbGwkcGF1bF9hbm5vW3doaWNoKGVuZHNXaXRoKG1ldGEuYWxsJHBhdWxfYW5ubywgIk1rIikpXSA8LSAiTWsiCmBgYAoKMikgR3JvdXAgQ2FweWJhcmEgYW5ub3RhdGlvbnMgdG9nZXRoZXIKYGBge3J9Cm1ldGEuYWxsJG1vcmVfZ2F0aGVyZWRfY3QgPC0gdW5saXN0KGxhcHBseShzdHJzcGxpdChtZXRhLmFsbCRjYWxsLCAiXyIpLCBmdW5jdGlvbih4KSB4W1sxXV0pKQptZXRhLmFsbCRtb3JlX2dhdGhlcmVkX2N0W3doaWNoKG1ldGEuYWxsJG1vcmVfZ2F0aGVyZWRfY3QgJWluJSBjKCJNb25vY3l0ZSIsICJNb25vY3l0ZS5wcm9nZW5pdG9yIiwgIk1vbm9jeXRlLnByb2dlbml0b3IuY2VsbCIpKV0gPC0gIk1vbm9jeXRlLnByb2dlbml0b3IiCm1ldGEuYWxsJG1vcmVfZ2F0aGVyZWRfY3Rbd2hpY2gobWV0YS5hbGwkbW9yZV9nYXRoZXJlZF9jdCAlaW4lIGMoIkVvc2lub3BoaWwucHJvZ2VuaXRvci5jZWxsIiwgIkVvc2lub3BoaWxzIikpXSA8LSAiRW9zaW5vcGhpbHMucHJvZ2VuaXRvciIKbWV0YS5hbGwkbW9yZV9nYXRoZXJlZF9jdFt3aGljaChtZXRhLmFsbCRtb3JlX2dhdGhlcmVkX2N0ICVpbiUgYygiUHJlLnByby5CLmNlbGwiLCAiQi5jZWxsIikpXSA8LSAiQi5jZWxsLnByb2dlbml0b3IiCm1ldGEuYWxsJG1vcmVfZ2F0aGVyZWRfY3Rbd2hpY2gobWV0YS5hbGwkbW9yZV9nYXRoZXJlZF9jdCAlaW4lIGMoIk11bHRpIikpXSA8LSAiSHlicmlkIgptZXRhLmFsbCRtb3JlX2dhdGhlcmVkX2N0W3doaWNoKG1ldGEuYWxsJG1vcmVfZ2F0aGVyZWRfY3QgJWluJSBjKCJIZW1hdG9wb2lldGljLnN0ZW0ucHJvZ2VuaXRvci5jZWxsIiwgIk11bHRpcG90ZW50LnByb2dlbml0b3IiKSldIDwtICJNUFAiCmBgYAoKMykgTG9vayBhdCBmcmVxdWVuY3kgb2YgZGlmZmVyZW50IGNlbGwgdHlwZXMKYGBge3J9CmZyZXEudGFibGUgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShtZXRhLmFsbCRtb3JlX2dhdGhlcmVkX2N0W3doaWNoKG1ldGEuYWxsJG1vcmVfZ2F0aGVyZWRfY3QgIT0gIkh5YnJpZCIpXSkgKiAxMDAvc3VtKHRhYmxlKG1ldGEuYWxsJG1vcmVfZ2F0aGVyZWRfY3Rbd2hpY2gobWV0YS5hbGwkbW9yZV9nYXRoZXJlZF9jdCAhPSAiSHlicmlkIildKSkpCmZyZXEudGFibGUgPC0gZnJlcS50YWJsZVtvcmRlcihmcmVxLnRhYmxlJEZyZXEsIGRlY3JlYXNpbmcgPSBUKSwgXQpmcmVxLnRhYmxlJFZhcjEgPC0gZmFjdG9yKGFzLmNoYXJhY3RlcihmcmVxLnRhYmxlJFZhcjEpLAogICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGFzLmNoYXJhY3RlcihmcmVxLnRhYmxlJFZhcjEpLAogICAgICAgICAgICAgICAgICAgICAgICAgIG9yZGVyZWQgPSBUKQoKZnJlcS50YWJsZS5zdWIgPC0gZnJlcS50YWJsZVt3aGljaChmcmVxLnRhYmxlJEZyZXEgPiAxLjUpLF0KZnJlcS50YWJsZS5zdWIgPC0gcmJpbmQoZnJlcS50YWJsZS5zdWIsIGRhdGEuZnJhbWUoVmFyMSA9ICJPdGhlciIsIEZyZXEgPSAoMTAwIC0gc3VtKGZyZXEudGFibGUuc3ViJEZyZXEpKSkpCgpnZ3Bsb3QoZnJlcS50YWJsZS5zdWIsIGFlcyh4ID0gVmFyMSwgeSA9IEZyZXEsIGZpbGwgPSBWYXIxKSkgKwogIGdlb21fYmFyKHBvc2l0aW9uID0gImRvZGdlIiwgc3RhdCA9ICJpZGVudGl0eSIpICsKICBzY2FsZV9maWxsX3ZpcmlkaXNfZChvcHRpb24gPSAiQSIsIGJlZ2luID0gMC4xNSwgZW5kID0gMC44NSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiLCBzaXplID0gMTIsIGFuZ2xlID0gOTAsIGhqdXN0ID0gMSksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwgc2l6ZSA9IDEyKSwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZC5pdGFsaWMiLCBzaXplID0gMTQpLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgdGl0bGUgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkLml0YWxpYyIsIHNpemUgPSAxNCksCiAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIpLAogICAgICAgIGF4aXMudGlja3MueCA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCgojIyMgQ2x1c3RlciBMYWJlbHMgYW5kIENvbXBhcmlzb24gdG8gUHJldmlvdXMgTGFiZWxzCkhlcmUgd2UgbGFiZWwgdGhlIGNsdXN0ZXJzIGJhc2VkIG9uIHRoZSBtb3N0IHJlcHJlc2VudGVkIHBvcHVsYXRpb24gaW4gZWFjaCBjbHVzdGVyLgoKMSkgQXNzaWduIGNsdXN0ZXIgbGFiZWxzIGJhc2VkIG9uIFBBR0EgYW5ub3RhdGlvbgoyKSBBc3NpZ24gY2x1c3RlciBsYWJlbHMgYmFzZWQgb24gQ2FweWJhcmEgYW5ub3RhdGlvbgpgYGB7cn0KbWV0YS5hbGwkbG91dmFpbi5sYWJlbCA8LSBOQQptZXRhLmFsbCRsb3V2YWluLmxhYmVsW3doaWNoKG1ldGEuYWxsJGxvdXZhaW4gJWluJSBjKDE2KSldIDwtICJTdGVtIgptZXRhLmFsbCRsb3V2YWluLmxhYmVsW3doaWNoKG1ldGEuYWxsJGxvdXZhaW4gJWluJSBjKDEwLDE3LDUsMykpXSA8LSAiRXJ5MCIKbWV0YS5hbGwkbG91dmFpbi5sYWJlbFt3aGljaChtZXRhLmFsbCRsb3V2YWluICVpbiUgYygxNSwgNikpXSA8LSAiRXJ5MSIKbWV0YS5hbGwkbG91dmFpbi5sYWJlbFt3aGljaChtZXRhLmFsbCRsb3V2YWluICVpbiUgYygxOCkpXSA8LSAiRXJ5MiIKbWV0YS5hbGwkbG91dmFpbi5sYWJlbFt3aGljaChtZXRhLmFsbCRsb3V2YWluICVpbiUgYygxMykpXSA8LSAiRXJ5MyIKbWV0YS5hbGwkbG91dmFpbi5sYWJlbFt3aGljaChtZXRhLmFsbCRsb3V2YWluICVpbiUgYyg3LDEyKSldIDwtICJFcnk0IgoKbWV0YS5hbGwkbG91dmFpbi5sYWJlbFt3aGljaChtZXRhLmFsbCRsb3V2YWluICVpbiUgYygyMCwgOCkpXSA8LSAiTUVQIgptZXRhLmFsbCRsb3V2YWluLmxhYmVsW3doaWNoKG1ldGEuYWxsJGxvdXZhaW4gJWluJSBjKDQsMCkpXSA8LSAiR01QIgptZXRhLmFsbCRsb3V2YWluLmxhYmVsW3doaWNoKG1ldGEuYWxsJGxvdXZhaW4gJWluJSBjKDIyKSldIDwtICJCYXNvIgoKbWV0YS5hbGwkbG91dmFpbi5sYWJlbFt3aGljaChtZXRhLmFsbCRsb3V2YWluICVpbiUgYygxOSwxNCwyKSldIDwtICJOZXUiCm1ldGEuYWxsJGxvdXZhaW4ubGFiZWxbd2hpY2gobWV0YS5hbGwkbG91dmFpbiAlaW4lIGMoMjQsOSwxLDExKSldIDwtICJNbyIKbWV0YS5hbGwkbG91dmFpbi5sYWJlbFt3aGljaChtZXRhLmFsbCRsb3V2YWluICVpbiUgYygyMykpXSA8LSAiREMiCm1ldGEuYWxsJGxvdXZhaW4ubGFiZWxbd2hpY2gobWV0YS5hbGwkbG91dmFpbiAlaW4lIGMoMjEpKV0gPC0gIkx5bXBoIgoKbWV0YS5hbGwkY2FweS5jbHVzdGVyLmxhYmVsIDwtIE5BCm1ldGEuYWxsJGNhcHkuY2x1c3Rlci5sYWJlbFt3aGljaChtZXRhLmFsbCRsb3V2YWluICVpbiUgYygwLDE2KSldIDwtICJNUFAvSFNQQyIKbWV0YS5hbGwkY2FweS5jbHVzdGVyLmxhYmVsW3doaWNoKG1ldGEuYWxsJGxvdXZhaW4gJWluJSBjKDEsOSwxMSwyNCkpXSA8LSAiTW9ub2N5dGUucHJvZ2VuaXRvciIKbWV0YS5hbGwkY2FweS5jbHVzdGVyLmxhYmVsW3doaWNoKG1ldGEuYWxsJGxvdXZhaW4gJWluJSBjKDIsMTQpKV0gPC0gIk5ldXRyb3BoaWwiCm1ldGEuYWxsJGNhcHkuY2x1c3Rlci5sYWJlbFt3aGljaChtZXRhLmFsbCRsb3V2YWluICVpbiUgYygzLDUsNiw3LDEyLDEzLDE4LDE1KSldIDwtICJFcnl0aHJvY3l0ZS5wcm9nZW5pdG9yIgptZXRhLmFsbCRjYXB5LmNsdXN0ZXIubGFiZWxbd2hpY2gobWV0YS5hbGwkbG91dmFpbiAlaW4lIGMoNCkpXSA8LSBtZXRhLmFsbCRtb3JlX2dhdGhlcmVkX2N0W3doaWNoKG1ldGEuYWxsJGxvdXZhaW4gJWluJSBjKDQpKV0KbWV0YS5hbGwkY2FweS5jbHVzdGVyLmxhYmVsW3doaWNoKG1ldGEuYWxsJGxvdXZhaW4gJWluJSBjKDgsMjApKV0gPC0gbWV0YS5hbGwkbW9yZV9nYXRoZXJlZF9jdFt3aGljaChtZXRhLmFsbCRsb3V2YWluICVpbiUgYyg4LDIwKSldCgptZXRhLmFsbCRjYXB5LmNsdXN0ZXIubGFiZWxbd2hpY2gobWV0YS5hbGwkbG91dmFpbiAlaW4lIGMoMTAsIDE3KSldIDwtICJFcnl0aHJvYmxhc3QiCm1ldGEuYWxsJGNhcHkuY2x1c3Rlci5sYWJlbFt3aGljaChtZXRhLmFsbCRsb3V2YWluICVpbiUgYygxOSkpXSA8LSBtZXRhLmFsbCRtb3JlX2dhdGhlcmVkX2N0W3doaWNoKG1ldGEuYWxsJGxvdXZhaW4gJWluJSBjKDE5KSldCm1ldGEuYWxsJGNhcHkuY2x1c3Rlci5sYWJlbFt3aGljaChtZXRhLmFsbCRsb3V2YWluICVpbiUgYygyMikpXSA8LSAiQmFzb3BoaWwiCm1ldGEuYWxsJGNhcHkuY2x1c3Rlci5sYWJlbFt3aGljaChtZXRhLmFsbCRsb3V2YWluICVpbiUgYygyMSkpXSA8LSAiTksuY2VsbCIKbWV0YS5hbGwkY2FweS5jbHVzdGVyLmxhYmVsW3doaWNoKG1ldGEuYWxsJGxvdXZhaW4gJWluJSBjKDIzKSldIDwtICJEQyIKYGBgCgozKSBIZXJlIHdlIGZ1cnRoZXIgY2xlYW4gdXAgdGhlIGh5YnJpZCBjZWxscyB0byBwcm9kdWNlIHRoZSBjb21wbGV0ZSBsaXN0IG9mIGNlbGxzIHdpdGggZGlzY3JldGUgaWRlbnRpdGllcy4KYGBge3J9Cm11bHRpLmNsYXNzaWZpY2F0aW9uLmxpc3QgPC0gbXVsdGkuaWQuY3VyYXRlLnFwKGJpbmFyeS5jb3VudHMgPSBiaW4uY291bnQsIGNsYXNzaWZpY2F0aW9uID0gY2xhc3NpZmljYXRpb24sIHFwLm1hdHJpeCA9IG10eC50ZXN0KQojIFJlYXNzaWduIHZhcmlhYmxlcwphY3R1YWwubXVsdGkgPC0gbXVsdGkuY2xhc3NpZmljYXRpb24ubGlzdFtbMV1dCm5ldy5jbGFzc2lmaWNhdGlvbiA8LSBtdWx0aS5jbGFzc2lmaWNhdGlvbi5saXN0W1syXV0KY29sbmFtZXMobmV3LmNsYXNzaWZpY2F0aW9uKSA8LSBjKCJiYyIsICJuZXcuY2FsbCIpCmBgYAoKNCkgQWRkIHRoZSBjb3JyZWN0ZWQgY2xhc3NpZmNhdGlvbiB0byB0aGUgbWV0YSBkYXRhCmBgYHtyfQptZXRhLmFsbCA8LSBjYmluZChtZXRhLmFsbCwgbmV3LmNsYXNzaWZpY2F0aW9uW3Jvd25hbWVzKG1ldGEuYWxsKSwgXSkKbWV0YS5hbGwkbW9yZV9nYXRoZXJlZF9jdF9uZXcgPC0gdW5saXN0KGxhcHBseShzdHJzcGxpdChtZXRhLmFsbCRuZXcuY2FsbCwgIl8iKSwgZnVuY3Rpb24oeCkgeFtbMV1dKSkKbWV0YS5hbGwkbW9yZV9nYXRoZXJlZF9jdF9uZXdbd2hpY2gobWV0YS5hbGwkbW9yZV9nYXRoZXJlZF9jdF9uZXcgJWluJSBjKCJNb25vY3l0ZSIsICJNb25vY3l0ZS5wcm9nZW5pdG9yIiwgIk1vbm9jeXRlLnByb2dlbml0b3IuY2VsbCIpKV0gPC0gIk1vbm9jeXRlLnByb2dlbml0b3IiCm1ldGEuYWxsJG1vcmVfZ2F0aGVyZWRfY3RfbmV3W3doaWNoKG1ldGEuYWxsJG1vcmVfZ2F0aGVyZWRfY3RfbmV3ICVpbiUgYygiRW9zaW5vcGhpbC5wcm9nZW5pdG9yLmNlbGwiLCAiRW9zaW5vcGhpbHMiKSldIDwtICJFb3Npbm9waGlscy5wcm9nZW5pdG9yIgptZXRhLmFsbCRtb3JlX2dhdGhlcmVkX2N0X25ld1t3aGljaChtZXRhLmFsbCRtb3JlX2dhdGhlcmVkX2N0X25ldyAlaW4lIGMoIlByZS5wcm8uQi5jZWxsIiwgIkIuY2VsbCIpKV0gPC0gIkIuY2VsbC5wcm9nZW5pdG9yIgptZXRhLmFsbCRtb3JlX2dhdGhlcmVkX2N0X25ld1t3aGljaChtZXRhLmFsbCRtb3JlX2dhdGhlcmVkX2N0X25ldyAlaW4lIGMoIk11bHRpIikpXSA8LSAiTXVsdGlfSUQiCm1ldGEuYWxsJG1vcmVfZ2F0aGVyZWRfY3RfbmV3W3doaWNoKG1ldGEuYWxsJG1vcmVfZ2F0aGVyZWRfY3RfbmV3ICVpbiUgYygiTXVsdGlwb3RlbnQucHJvZ2VuaXRvciIsIkhlbWF0b3BvaWV0aWMuc3RlbS5wcm9nZW5pdG9yLmNlbGwiKSldIDwtICJNUFAvSFNQQyIKYGBgCgo1KSBUbyBhc3Nlc3MgdGhlIGRpc2NyZXRlIGNlbGxzIG9ubHksIHdlIHJlbW92ZSB0aGUgaHlicmlkIGNlbGxzIGZvciBub3cuCmBgYHtyfQptZXRhLmFsbC5uby5tdWx0aSA8LSBtZXRhLmFsbFt3aGljaChtZXRhLmFsbCRuZXcuY2FsbCAhPSAiTXVsdGlfSUQiKSwgXQptZXRhLmFsbC50YWJsZSA8LSB0YWJsZShtZXRhLmFsbC5uby5tdWx0aSRjYXB5LmNsdXN0ZXIubGFiZWwsIG1ldGEuYWxsLm5vLm11bHRpJGxvdXZhaW4ubGFiZWwpCm1ldGEuYWxsLnRhYmxlIDwtIGFzLmRhdGEuZnJhbWUoYXBwbHkobWV0YS5hbGwudGFibGUsIDIsIGZ1bmN0aW9uKHgpIHJvdW5kKHggKjEwMC9zdW0oeCksIGRpZ2l0cyA9IDMpKSkKbWV0YS5hbGwudGFibGUkY2FweS5jYWxsIDwtIHJvd25hbWVzKG1ldGEuYWxsLnRhYmxlKSAKYGBgCgo2KSBIZWF0bWFwIHBsb3QgYW5kIGNvbXBhcmlzb24KYGBge3J9Cm1ldGEubWVsdCA8LSByZXNoYXBlMjo6bWVsdChtZXRhLmFsbC50YWJsZSkKbWV0YS5tZWx0IDwtIG1ldGEubWVsdFt3aGljaChtZXRhLm1lbHQkY2FweS5jYWxsICE9ICJIeWJyaWQiKSxdCm1ldGEubWVsdCA8LSBtZXRhLm1lbHRbd2hpY2gobWV0YS5tZWx0JGNhcHkuY2FsbCAhPSAiTVBQIiksXQoKbWV0YS5tZWx0JHZhcmlhYmxlIDwtIGZhY3RvcihtZXRhLm1lbHQkdmFyaWFibGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIkVyeTAiLCAiRXJ5MSIsICJFcnkyIiwiRXJ5MyIsICJFcnk0IiwiTUVQIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQmFzbyIsICJNbyIsICJOZXUiLCAiTHltcGgiLCAiR01QIiwgICJTdGVtIiwgIkRDIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3JkZXJlZCA9IFQpCgptZXRhLm1lbHQkY2FweS5jYWxsIDwtIGZhY3RvcihtZXRhLm1lbHQkY2FweS5jYWxsLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJFcnl0aHJvYmxhc3QiLCAiRXJ5dGhyb2N5dGUucHJvZ2VuaXRvciIsICJNZWdha2FyeW9jeXRlLnByb2dlbml0b3IuY2VsbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkJhc29waGlsIiwgIk1vbm9jeXRlLnByb2dlbml0b3IiLCAiTmV1dHJvcGhpbCIsICJOSy5jZWxsIiwgIk1QUC9IU1BDIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiREMiLCAiQi5jZWxsLnByb2dlbml0b3IiLCAiRW9zaW5vcGhpbHMucHJvZ2VuaXRvciIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvcmRlcmVkID0gVCkKCmdncGxvdChtZXRhLm1lbHQsIGFlcyh4ID0gdmFyaWFibGUsIHkgPSBjYXB5LmNhbGwsIGZpbGwgPSB2YWx1ZSkpICsKICBnZW9tX3RpbGUoKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzX2Mob3B0aW9uID0gIkEiLCBuYW1lID0gInBlcmNlbnRhZ2UiLCBiZWdpbiA9IDAuMTUsIGVuZCA9IDAuODUpICsKICBnZ3RpdGxlKCJQYXVsIGV0IGFsLiAyMDE1IikgKwogIGxhYnMoeCA9ICJPcmlnaW5hbCBBbm5vdGF0aW9uIiwgeSA9ICJDYXB5YmFyYSBBbm5vdGF0aW9uIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0iYm90dG9tIiwKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9MSwgZmFjZSA9ICJib2xkIiwgc2l6ZSA9IDEyKSwKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiLCBzaXplID0gMTIpLAogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQuaXRhbGljIiwgc2l6ZSA9IDE0KSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkLml0YWxpYyIsIHNpemUgPSAxNCksCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICB0aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQuaXRhbGljIiwgc2l6ZSA9IDE0KSwKICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCgpPdmVyYWxsLCB3ZSBhcmUgY29ycmVzcG9uZGluZyB3ZWxsIHRvIHRoZSBQYXVsIGV0IGFsICYgUEFHQSBhbm5vdGF0aW9uLiBXZSBuZXh0IGNhcmVmdWxseSBhc3Nlc3MgdGhlIGh5YnJpZCBjZWxscywgbGV2ZXJhZ2luZyBwc2V1ZG90aW1lIGluZm9ybWF0aW9uIGZyb20gUEFHQS4KCiMjIyBQc2V1ZG90aW1lCiMjIyMgRGlzY3JldGUgSWRlbnRpdGllcwpGaXJzdCwgd2UgY2hlY2sgdGhlIHBzZXVkb3RpbWUgb2YgdGhlIGRpc2NyZXRlIGNlbGwgdHlwZXMgY2xhc3NpZmllZCBpbiB0aGUgZGF0YXNldCBhcyBhbm90aGVyIGJlbmNobWFya2luZyBtZXRyaWMgdG8gZXZhbHVhdGUgdGhlIGVmZmljYWN5IG9mIHRoZSBjbGFzc2lmaWNhdGlvbiwgd2hlcmUgd2Ugc2VlIEhTUENzIG9jY3VwdWluZyB0aGUgZWFybGllc3QgcHNldWRvdGltZS4KCmBgYHtyLCBmaWcud2lkdGg9NSwgaGVpZ2h0ID0gOCwgd2FybmluZz1GQUxTRX0KbWVkaWFuLnF1YXJ0aWxlIDwtIGZ1bmN0aW9uKHgpewogIG91dCA8LSBxdWFudGlsZSh4LCBwcm9icyA9IGMoMC4yNSwwLjUsMC43NSkpCiAgbmFtZXMob3V0KSA8LSBjKCJ5bWluIiwieSIsInltYXgiKQogIHJldHVybihvdXQpIAp9Cm1ldGEuc3ViLmZvci5wc2V1ZG90aW1lIDwtIG1ldGEuYWxsWy13aGljaChtZXRhLmFsbCRtb3JlX2dhdGhlcmVkX2N0X25ldyAlaW4lIGMoIkRlbmRyaXRpYy5jZWxsIiwgIk5LLmNlbGwiLCAiTXVsdGlfSUQiLCAiQi5jZWxsLnByb2dlbml0b3IiKSksIF0KbWV0YS5zdWIuZm9yLnBzZXVkb3RpbWUkbW9yZV9nYXRoZXJlZF9jdF9uZXcgPC0gZmFjdG9yKG1ldGEuc3ViLmZvci5wc2V1ZG90aW1lJG1vcmVfZ2F0aGVyZWRfY3RfbmV3LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJNUFAvSFNQQyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJNZWdha2FyeW9jeXRlLnByb2dlbml0b3IuY2VsbCIsICJCYXNvcGhpbCIsICJFb3Npbm9waGlscy5wcm9nZW5pdG9yIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTW9ub2N5dGUucHJvZ2VuaXRvciIsICJOZXV0cm9waGlsIiwgIk1hY3JvcGhhZ2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJFcnl0aHJvY3l0ZS5wcm9nZW5pdG9yIiwgIkVyeXRocm9ibGFzdCIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvcmRlcmVkID0gVCkKY3MgPC0gdmlyaWRpcygyMCkKZ2dwbG90KG1ldGEuc3ViLmZvci5wc2V1ZG90aW1lLCBhZXMoeCA9IG1vcmVfZ2F0aGVyZWRfY3RfbmV3LCB5ID0gZHB0X3BzZXVkb3RpbWUsIGZpbGwgPSBtb3JlX2dhdGhlcmVkX2N0X25ldykpICsKICBnZW9tX3Zpb2xpbihzY2FsZSA9ICJ3aWR0aCIpICsKICBzdGF0X3N1bW1hcnkoZnVuLnk9bWVkaWFuLnF1YXJ0aWxlLGdlb209J3BvaW50JywgY29sb3IgPSByZXAoY3NbYygyMCwgMjAsIDIwLCAyMCwgMjAsIDEsMSwxLDEpXSwgZWFjaCA9IDMpKSArCiAgc3RhdF9zdW1tYXJ5KGZ1bi55PW1lZGlhbi5xdWFydGlsZSxnZW9tPSdsaW5lJywgY29sb3IgPSByZXAoY3NbYygyMCwgMjAsIDIwLCAyMCwgMjAsIDEsMSwxLDEpXSwgZWFjaCA9IDMpKSArCiAgZ2VvbV9qaXR0ZXIoY29sb3IgPSAiZ3JleSIsIHNpemUgPSAwLjEpICsKICBzY2FsZV9maWxsX3ZpcmlkaXNfZChvcHRpb24gPSAiQSIpICsKICBjb29yZF9mbGlwKCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIsCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkLml0YWxpYyIpLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIpLAogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICB0aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQuaXRhbGljIiwgc2l6ZSA9IDE0KSwKICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiwgc2l6ZSA9IDEpKQpgYGAKCiMjIyMgSHlicmlkIENlbGxzCklkZWFsbHksIGh5YnJpZCBjZWxscyBzaG91bGQgaGF2ZSBhIHBzZXVkb3RpbWUgcmFuZ2UgaW4gYmV0d2VlbiB0aGVpciBvcmlnaW4gYW5kIGRlc3RpbmF0aW9uIGNlbGwgc3RhdGVzLiBUaGVyZWZvcmUsIHdlIGludmVzdGlnYXRlIHRoZSBwc2V1ZG90aW1lIGRpc3RyaWJ1dGlvbiBvZiB0aGVzZSBjZWxscyBhbmQgdGhlaXIgZGlzY3JldGUgY291bnRlcnBhcnRzLgoKMSkgV2UgZmlyc3QgaW5wdXQgdGhlIHBzZXVkb3RpbWUgZm9yIHRoZSBkaXNjcmV0ZSBjZWxsIHR5cGVzIHRoYXQgY29tcG9zZSB0aGUgaHlicmlkcyBpbiBhIGRhdGEgZnJhbWUKYGBge3J9CnBzZXVkb3RpbWUuZm9yLmVhY2guY2F0ZWdvcnkgPC0gbWV0YS5hbGxbLXdoaWNoKG1ldGEuYWxsJG1vcmVfZ2F0aGVyZWRfY3RfbmV3ID09ICJNdWx0aV9JRCIgfCBtZXRhLmFsbCRkcHRfcHNldWRvdGltZSA9PSBJbmYpLCBdCnBzZXVkb3RpbWUuZHQgPC0gcHNldWRvdGltZS5mb3IuZWFjaC5jYXRlZ29yeVssYyg1LDE2LDIwKV0KbWVhbi5wc2V1ZG90aW1lLmR0IDwtIGMoKQp1bmlxdWUuY3QgPC0gdW5pcXVlKHBzZXVkb3RpbWUuZm9yLmVhY2guY2F0ZWdvcnkkbW9yZV9nYXRoZXJlZF9jdF9uZXcpCmZvciAoY3QgaW4gdW5pcXVlLmN0KSB7CiAgbWVhbi5wc2V1ZG90aW1lLmR0W2N0XSA8LSBtZWFuKHBzZXVkb3RpbWUuZm9yLmVhY2guY2F0ZWdvcnlbd2hpY2gocHNldWRvdGltZS5mb3IuZWFjaC5jYXRlZ29yeSRtb3JlX2dhdGhlcmVkX2N0X25ldyA9PSBjdCksICJkcHRfcHNldWRvdGltZSJdKQp9CmBgYAoKMikgV2UgbmV4dCBpc29sYXRlIHBzZXVkb3RpbWUgZm9yIHRoZSBoeWJyaWRzCmBgYHtyfQpjdC5wc2V1ZG8gPC0gYXMuZGF0YS5mcmFtZShtZWFuLnBzZXVkb3RpbWUuZHQpCm11bHRpLmlkLnBzZXVkbyA8LSBhY3R1YWwubXVsdGkKbXVsdGkuaWQucHNldWRvJHBzZXVkb3RpbWUgPC0gbWV0YS5hbGxbYWN0dWFsLm11bHRpJGNlbGwuYmMsICJkcHRfcHNldWRvdGltZSJdCm11bHRpLmlkLnBzZXVkbyA8LSBtdWx0aS5pZC5wc2V1ZG9bLXdoaWNoKG11bHRpLmlkLnBzZXVkbyRwc2V1ZG90aW1lID09IEluZiksXQptdWx0aS5pZC5wc2V1ZG8kY3Qub25seSA8LSBnc3ViKCJmcnhuX2NlbGwudHlwZV8iLCAiIiwgbXVsdGkuaWQucHNldWRvJHZhcmlhYmxlKQptdWx0aS5pZC5wc2V1ZG8kY3Qub25seSA8LSB1bmxpc3QobGFwcGx5KHN0cnNwbGl0KG11bHRpLmlkLnBzZXVkbyRjdC5vbmx5LCAiXyIpLCBmdW5jdGlvbih4KSB4WzFdKSkKCm11bHRpLmlkLnBzZXVkbyRjdC5vbmx5LmF2Zy5wc2V1ZG8gPC0gY3QucHNldWRvW211bHRpLmlkLnBzZXVkbyRjdC5vbmx5LCAibWVhbi5wc2V1ZG90aW1lLmR0Il0KbXVsdGkuaWQucHNldWRvIDwtIG11bHRpLmlkLnBzZXVkb1shaXMubmEobXVsdGkuaWQucHNldWRvJGN0Lm9ubHkuYXZnLnBzZXVkbyksIF0KYGBgCgozKSBNb3JlIGRldGFpbGVkIGJyZWFrIGRvd24gb2YgdGhlIGh5YnJpZHMKYGBge3J9CmNlbGwudGFibGUgPC0gZGF0YS5mcmFtZSgpCmNlbGwudW5pcSA8LSB1bmlxdWUobXVsdGkuaWQucHNldWRvJGNlbGwuYmMpCmZvciAoY3Vyci5jIGluIGNlbGwudW5pcSkgewogIGN0IDwtIG11bHRpLmlkLnBzZXVkb1t3aGljaChtdWx0aS5pZC5wc2V1ZG8kY2VsbC5iYyA9PSBjdXJyLmMpLCAiY3Qub25seSJdCiAgY3Rbd2hpY2goY3QgPT0gIk1vbm9jeXRlIildIDwtICJNb25vY3l0ZS5wcm9nZW5pdG9yIgogIAogIGlmIChsZW5ndGgodW5pcXVlKGN0KSkgPiAxICYKICAgICAgbGVuZ3RoKHVuaXF1ZShjdCkpID09IDIpIHsKICAgIAogICAgY3Vyci5kZiA8LSBkYXRhLmZyYW1lKGNlbGwuYmMgPSBjdXJyLmMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgaWRlbnRpdHkgPSBwYXN0ZTAoc29ydCh1bmlxdWUoY3QpKSwgY29sbGFwc2UgPSAiLSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgIHBzZXVkbyA9IG1lYW4obXVsdGkuaWQucHNldWRvW3doaWNoKG11bHRpLmlkLnBzZXVkbyRjZWxsLmJjID09IGN1cnIuYyksICJwc2V1ZG90aW1lIl0pLAogICAgICAgICAgICAgICAgICAgICAgICAgIG1pbi5yYW5nZSA9IG1pbihtdWx0aS5pZC5wc2V1ZG9bd2hpY2gobXVsdGkuaWQucHNldWRvJGNlbGwuYmMgPT0gY3Vyci5jKSwgImN0Lm9ubHkuYXZnLnBzZXVkbyJdKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBtYXgucmFuZ2UgPSBtYXgobXVsdGkuaWQucHNldWRvW3doaWNoKG11bHRpLmlkLnBzZXVkbyRjZWxsLmJjID09IGN1cnIuYyksICJjdC5vbmx5LmF2Zy5wc2V1ZG8iXSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCiAgICAKICAgIGlmIChucm93KGNlbGwudGFibGUpIDw9IDApIHsKICAgICAgY2VsbC50YWJsZSA8LSBjdXJyLmRmCiAgICB9IGVsc2UgewogICAgICBjZWxsLnRhYmxlIDwtIHJiaW5kKGNlbGwudGFibGUsIGN1cnIuZGYpCiAgICB9CiAgfQp9CmBgYAoKNCkgSWRlbnRpZnkgdGhlIG1ham9yIGh5YnJpZCBwb3B1bGF0aW9ucy4KYGBge3IsIGZpZy5oZWlnaHQ9OH0KZnJlcS50YWJsZS5uZXcgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShjZWxsLnRhYmxlJGlkZW50aXR5KSkKZnJlcS50YWJsZS5uZXcgPC0gZnJlcS50YWJsZS5uZXdbb3JkZXIoZnJlcS50YWJsZS5uZXckRnJlcSwgZGVjcmVhc2luZyA9IFQpLCBdCgpmcmVxLnRhYmxlIDwtIGFzLmRhdGEuZnJhbWUodGFibGUoY2VsbC50YWJsZSRpZGVudGl0eSkgKiAxMDAvc3VtKHRhYmxlKGNlbGwudGFibGUkaWRlbnRpdHkpKSkKZnJlcS50YWJsZSA8LSBmcmVxLnRhYmxlW29yZGVyKGZyZXEudGFibGUkRnJlcSwgZGVjcmVhc2luZyA9IFQpLCBdCmZyZXEudGFibGUkVmFyMSA8LSBmYWN0b3IoYXMuY2hhcmFjdGVyKGZyZXEudGFibGUkVmFyMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYXMuY2hhcmFjdGVyKGZyZXEudGFibGUkVmFyMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgb3JkZXJlZCA9IFQpCgpmcmVxLnRhYmxlLnN1YiA8LSBmcmVxLnRhYmxlW3doaWNoKGZyZXEudGFibGUkRnJlcSA+IDEwMDAvMjU3KSxdCmZyZXEudGFibGUuc3ViJEZyZXEgPC0gZnJlcS50YWJsZS5zdWIkRnJlcSAqIDEwMC9zdW0oZnJlcS50YWJsZS5zdWIkRnJlcSkKCmdncGxvdChmcmVxLnRhYmxlLnN1YiwgYWVzKHggPSBWYXIxLCB5ID0gRnJlcSwgZmlsbCA9IFZhcjEpKSArCiAgZ2VvbV9iYXIocG9zaXRpb24gPSAiZG9kZ2UiLCBzdGF0ID0gImlkZW50aXR5IikgKwogIHNjYWxlX2ZpbGxfdmlyaWRpc19kKG9wdGlvbiA9ICJBIiwgYmVnaW4gPSAwLjE1LCBlbmQgPSAwLjg1KSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsIHNpemUgPSAxMiwgYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSwKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiLCBzaXplID0gMTIpLAogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkLml0YWxpYyIsIHNpemUgPSAxNCksCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICB0aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQuaXRhbGljIiwgc2l6ZSA9IDE0KSwKICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiksCiAgICAgICAgYXhpcy50aWNrcy54ID0gZWxlbWVudF9ibGFuaygpKQpgYGAKCjUpIEZpbHRlciB0aGUgaHlicmlkcyB0byB0aGUgZml2ZSBtYWpvciBwb3B1bGF0aW9ucyBmb3IgdGhlIHBzZXVkb3RpbWUgY29tcGFyaXNvbgpgYGB7cn0KY2VsbC50YWJsZS5zdWIgPC0gY2VsbC50YWJsZVt3aGljaChjZWxsLnRhYmxlJGlkZW50aXR5ICVpbiUgYygiRXJ5dGhyb2JsYXN0LUVyeXRocm9jeXRlLnByb2dlbml0b3IiLCAiTW9ub2N5dGUucHJvZ2VuaXRvci1OZXV0cm9waGlsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRXJ5dGhyb2N5dGUucHJvZ2VuaXRvci1NZWdha2FyeW9jeXRlLnByb2dlbml0b3IuY2VsbCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJFb3Npbm9waGlscy1Nb25vY3l0ZS5wcm9nZW5pdG9yIiwgIkVvc2lub3BoaWxzLU1lZ2FrYXJ5b2N5dGUucHJvZ2VuaXRvci5jZWxsIikpLCBdCm1ldGEuYWxsLm11bHRpLmNlbGxzIDwtIG1ldGEuYWxsW3doaWNoKHJvd25hbWVzKG1ldGEuYWxsKSAlaW4lIGNlbGwudGFibGUuc3ViJGNlbGwuYmMpLCBdCnJvd25hbWVzKGNlbGwudGFibGUuc3ViKSA8LSBjZWxsLnRhYmxlLnN1YiRjZWxsLmJjCm1ldGEuYWxsLm11bHRpLmNlbGxzJG11bHRpLmJyZWFrLmRvd24gPC0gY2VsbC50YWJsZS5zdWJbcm93bmFtZXMobWV0YS5hbGwubXVsdGkuY2VsbHMpLCAiaWRlbnRpdHkiXQpgYGAKCjYpIFdlIGZpcnN0IGxvb2sgYXQgdGhlIGxhcmdlc3QgaHlicmlkIHBvcHVsYXRpb246IGVyeXRocm9ibGFzdC1lcnl0aHJvY3l0ZSBwcm9nZW5pdG9yIGh5YnJpZHMKYGBge3J9CnVucS5jdCA8LSB1bmlxdWUobWV0YS5hbGwkbW9yZV9nYXRoZXJlZF9jdF9uZXcpCmxhYmVsX2RmIDwtIGRhdGEuZnJhbWUoKQpmb3IgKGN1cnIuY3QgaW4gdW5xLmN0KSB7CiAgY3Vycl9zdWIgPC0gbWV0YS5hbGxbd2hpY2gobWV0YS5hbGwkbW9yZV9nYXRoZXJlZF9jdF9uZXcgPT0gY3Vyci5jdCksXQogIGN1cnJfdjEgPC0gbWVhbihjdXJyX3N1YiRWMSkKICBjdXJyX3YyIDwtIG1lYW4oY3Vycl9zdWIkVjIpCiAgY3Vycl9kZiA8LSBkYXRhLmZyYW1lKFYxID0gY3Vycl92MSwgVjIgPSBjdXJyX3YyLCBjZWxsLnR5cGUgPSBjdXJyLmN0LCBzdHJpbmdzQXNGYWN0b3JzID0gRikKICBpZiAobnJvdyhsYWJlbF9kZikgPD0gMCkgewogICAgbGFiZWxfZGYgPC0gY3Vycl9kZgogIH0gZWxzZSB7CiAgICBsYWJlbF9kZiA8LSByYmluZChsYWJlbF9kZiwgY3Vycl9kZikKICB9Cn0KbGFiZWxfZGZfbm9fbXVsdGkgPC0gbGFiZWxfZGZbLXdoaWNoKGxhYmVsX2RmJGNlbGwudHlwZSA9PSAiTXVsdGlfSUQiKSxdCm1ldGEuYWxsJGNlbGwudHlwZSA8LSBtZXRhLmFsbCRtb3JlX2dhdGhlcmVkX2N0X25ldwpgYGAKCmBgYHtyfQptZXRhLmFsbFssICJlcnkuZXJ5Lm11bHRpIl0gPC0gMAptZXRhLmFsbFtjZWxsLnRhYmxlW3doaWNoKGNlbGwudGFibGUkaWRlbnRpdHkgPT0gIkVyeXRocm9ibGFzdC1Fcnl0aHJvY3l0ZS5wcm9nZW5pdG9yIiksICJjZWxsLmJjIl0sICJlcnkuZXJ5Lm11bHRpIl0gPC0gMQpgYGAKCmBgYHtyLCBmaWcud2lkdGg9NywgZmlnLmhlaWdodD03LjV9CmxpYnJhcnkoZ2dmb3JjZSkKZ2dwbG90KGxhYmVsX2RmX25vX211bHRpW3doaWNoKGxhYmVsX2RmX25vX211bHRpJGNlbGwudHlwZSAlaW4lIGMoIkVyeXRocm9ibGFzdCIsICJFcnl0aHJvY3l0ZS5wcm9nZW5pdG9yIikpLF0sIGFlcyh4ID0gVjEsIHkgPSBWMiwgbGFiZWwgPSBjZWxsLnR5cGUsIGNvbG9yID0gY2VsbC50eXBlKSkgKwogIGdlb21fcG9pbnQoZGF0YSA9IG1ldGEuYWxsLCBjb2xvciA9ICJsaWdodGdyZXkiKSArCiAgZ2VvbV9wb2ludChkYXRhID0gbWV0YS5hbGxbd2hpY2gobWV0YS5hbGwkbW9yZV9nYXRoZXJlZF9jdF9uZXcgJWluJSBjKCJFcnl0aHJvYmxhc3QiLCAiRXJ5dGhyb2N5dGUucHJvZ2VuaXRvciIpKSxdLCBhZXMoY29sb3IgPSBtb3JlX2dhdGhlcmVkX2N0X25ldykpICsKICBnZW9tX2NpcmNsZShkYXRhID0gbWV0YS5hbGxbd2hpY2gobWV0YS5hbGwkZXJ5LmVyeS5tdWx0aSA9PSAxKSwgXSwgbWFwcGluZyA9IGFlcyh4MCA9IFYxLCB5MCA9IFYyLCByID0gMTAwKSwgZmlsbCA9ICJkYXJrZ3JleSIsIGluaGVyaXQuYWVzID0gRikgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBSQ29sb3JCcmV3ZXI6OmJyZXdlci5wYWwoMTIsICJQYWlyZWQiKVtjKDUsNildKSArCiAgZ2VvbV90ZXh0X3JlcGVsKGJveC5wYWRkaW5nID0gMC41LCBtYXgub3ZlcmxhcHMgPSBJbmYsIGNvbG9yID0gImJsYWNrIikgKwogICNnZW9tX3BvaW50KCkgKyAKICBsYWJzKHggPSAiRkExIiwgeSA9ICJGQTIiKSArCiAgZ2d0aXRsZSgiQ2FweWJhcmEgQW5ub3RhdGlvbjogRXJ5dGhyb2JsYXN0ICYgRXJ5dGhyb2N5dGUgUHJvZ2VuaXRvciIpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiLAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkLml0YWxpYyIsIHNpemUgPSAxNCksIAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgdGl0bGUgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkLml0YWxpYyIsIHNpemUgPSAxNCksCiAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIsIHNpemUgPSAwLjUpLAogICAgICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCkpCmBgYAo3KSBXZSBuZXh0IGxvb2sgYXQgYWxsIHRoZSBvdGhlciBoeWJyaWRzCgpJKSBNb25vY3l0ZS5wcm9nZW5pdG9yLU5ldXRyb3BoaWwgaHlicmlkcwpgYGB7cixmaWcud2lkdGg9NywgZmlnLmhlaWdodD03LjV9CmdncGxvdChsYWJlbF9kZl9ub19tdWx0aVt3aGljaChsYWJlbF9kZl9ub19tdWx0aSRjZWxsLnR5cGUgJWluJSBjKCJNb25vY3l0ZS5wcm9nZW5pdG9yIiwgIk5ldXRyb3BoaWwiKSksXSwgYWVzKHggPSBWMSwgeSA9IFYyLCBsYWJlbCA9IGNlbGwudHlwZSwgY29sb3IgPSBjZWxsLnR5cGUpKSArCiAgZ2VvbV9wb2ludChkYXRhID0gbWV0YS5hbGwsIGNvbG9yID0gImxpZ2h0Z3JleSIpICsKICBnZW9tX3BvaW50KGRhdGEgPSBtZXRhLmFsbFt3aGljaChtZXRhLmFsbCRtb3JlX2dhdGhlcmVkX2N0X25ldyAlaW4lIGMoIk1vbm9jeXRlLnByb2dlbml0b3IiLCAiTmV1dHJvcGhpbCIpKSxdLCBhZXMoY29sb3IgPSBtb3JlX2dhdGhlcmVkX2N0X25ldykpICsKICBnZW9tX2NpcmNsZShkYXRhID0gbWV0YS5hbGxbY2VsbC50YWJsZVt3aGljaChjZWxsLnRhYmxlJGlkZW50aXR5ID09ICJNb25vY3l0ZS5wcm9nZW5pdG9yLU5ldXRyb3BoaWwiKSwgImNlbGwuYmMiXSwgXSwgbWFwcGluZyA9IGFlcyh4MCA9IFYxLCB5MCA9IFYyLCByID0gMTAwKSwgZmlsbCA9ICJkYXJrZ3JleSIsIGluaGVyaXQuYWVzID0gRikgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBSQ29sb3JCcmV3ZXI6OmJyZXdlci5wYWwoMTIsICJQYWlyZWQiKVtjKDUsNildKSArCiAgZ2VvbV90ZXh0X3JlcGVsKGJveC5wYWRkaW5nID0gMC41LCBtYXgub3ZlcmxhcHMgPSBJbmYsIGNvbG9yID0gImJsYWNrIikgKwogICNnZW9tX3BvaW50KCkgKyAKICBsYWJzKHggPSAiRkExIiwgeSA9ICJGQTIiKSArCiAgZ2d0aXRsZSgiQ2FweWJhcmEgQW5ub3RhdGlvbjogTW9ub2N5dGUgUHJvZ2VuaXRvciAmIE5ldXRyb3BoaWwiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiwKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZC5pdGFsaWMiLCBzaXplID0gMTQpLCAKICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgIHRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZC5pdGFsaWMiLCBzaXplID0gMTQpLAogICAgICAgIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siLCBzaXplID0gMC41KSwKICAgICAgICBheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpKQpgYGAKCklJKSBFcnl0aHJvY3l0ZS5wcm9nZW5pdG9yLU1lZ2FrYXJ5b2N5dGUucHJvZ2VuaXRvci5jZWxsIGh5YnJpZHMKYGBge3IsIGZpZy53aWR0aD03LCBmaWcuaGVpZ2h0PTcuNX0KZ2dwbG90KGxhYmVsX2RmX25vX211bHRpW3doaWNoKGxhYmVsX2RmX25vX211bHRpJGNlbGwudHlwZSAlaW4lIGMoIkVyeXRocm9jeXRlLnByb2dlbml0b3IiLCAiTWVnYWthcnlvY3l0ZS5wcm9nZW5pdG9yLmNlbGwiKSksXSwgYWVzKHggPSBWMSwgeSA9IFYyLCBsYWJlbCA9IGNlbGwudHlwZSwgY29sb3IgPSBjZWxsLnR5cGUpKSArCiAgZ2VvbV9wb2ludChkYXRhID0gbWV0YS5hbGwsIGNvbG9yID0gImxpZ2h0Z3JleSIpICsKICBnZW9tX3BvaW50KGRhdGEgPSBtZXRhLmFsbFt3aGljaChtZXRhLmFsbCRtb3JlX2dhdGhlcmVkX2N0X25ldyAlaW4lIGMoIkVyeXRocm9jeXRlLnByb2dlbml0b3IiLCAiTWVnYWthcnlvY3l0ZS5wcm9nZW5pdG9yLmNlbGwiKSksXSwgYWVzKGNvbG9yID0gbW9yZV9nYXRoZXJlZF9jdF9uZXcpKSArCiAgZ2VvbV9jaXJjbGUoZGF0YSA9IG1ldGEuYWxsW2NlbGwudGFibGVbd2hpY2goY2VsbC50YWJsZSRpZGVudGl0eSA9PSAiRXJ5dGhyb2N5dGUucHJvZ2VuaXRvci1NZWdha2FyeW9jeXRlLnByb2dlbml0b3IuY2VsbCIpLCAiY2VsbC5iYyJdLCBdLCBtYXBwaW5nID0gYWVzKHgwID0gVjEsIHkwID0gVjIsIHIgPSAxMDApLCBmaWxsID0gImRhcmtncmV5IiwgaW5oZXJpdC5hZXMgPSBGKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IFJDb2xvckJyZXdlcjo6YnJld2VyLnBhbCgxMiwgIlBhaXJlZCIpW2MoNSw2KV0pICsKICBnZW9tX3RleHRfcmVwZWwoYm94LnBhZGRpbmcgPSAwLjUsIG1heC5vdmVybGFwcyA9IEluZiwgY29sb3IgPSAiYmxhY2siKSArCiAgI2dlb21fcG9pbnQoKSArIAogIGxhYnMoeCA9ICJGQTEiLCB5ID0gIkZBMiIpICsKICBnZ3RpdGxlKCJDYXB5YmFyYSBBbm5vdGF0aW9uOiBNZWdha2FyeW9jeXRlIFByb2dlbml0b3IgJiBFcnl0aHJvY3l0ZSBQcm9nZW5pdG9yIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIsCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQuaXRhbGljIiwgc2l6ZSA9IDE0KSwgCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICB0aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQuaXRhbGljIiwgc2l6ZSA9IDE0KSwKICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiwgc2l6ZSA9IDAuNSksCiAgICAgICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCgpJSUkpIEVvc2lub3BoaWxzLnByb2dlbml0b3ItTWVnYWthcnlvY3l0ZS5wcm9nZW5pdG9yLmNlbGwgaHlicmlkcwpgYGB7ciwgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9Ny41fQpnZ3Bsb3QobGFiZWxfZGZfbm9fbXVsdGlbd2hpY2gobGFiZWxfZGZfbm9fbXVsdGkkY2VsbC50eXBlICVpbiUgYygiRW9zaW5vcGhpbHMucHJvZ2VuaXRvciIsICJNZWdha2FyeW9jeXRlLnByb2dlbml0b3IuY2VsbCIpKSxdLCBhZXMoeCA9IFYxLCB5ID0gVjIsIGxhYmVsID0gY2VsbC50eXBlLCBjb2xvciA9IGNlbGwudHlwZSkpICsKICBnZW9tX3BvaW50KGRhdGEgPSBtZXRhLmFsbCwgY29sb3IgPSAibGlnaHRncmV5IikgKwogIGdlb21fcG9pbnQoZGF0YSA9IG1ldGEuYWxsW3doaWNoKG1ldGEuYWxsJG1vcmVfZ2F0aGVyZWRfY3RfbmV3ICVpbiUgYygiRW9zaW5vcGhpbHMucHJvZ2VuaXRvciIsICJNZWdha2FyeW9jeXRlLnByb2dlbml0b3IuY2VsbCIpKSxdLCBhZXMoY29sb3IgPSBtb3JlX2dhdGhlcmVkX2N0X25ldykpICsKICBnZW9tX2NpcmNsZShkYXRhID0gbWV0YS5hbGxbY2VsbC50YWJsZVt3aGljaChjZWxsLnRhYmxlJGlkZW50aXR5ID09ICJFb3Npbm9waGlscy1NZWdha2FyeW9jeXRlLnByb2dlbml0b3IuY2VsbCIpLCAiY2VsbC5iYyJdLCBdLCBtYXBwaW5nID0gYWVzKHgwID0gVjEsIHkwID0gVjIsIHIgPSAxMDApLCBmaWxsID0gImRhcmtncmV5IiwgaW5oZXJpdC5hZXMgPSBGKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IFJDb2xvckJyZXdlcjo6YnJld2VyLnBhbCgxMiwgIlBhaXJlZCIpW2MoNSw2KV0pICsKICBnZW9tX3RleHRfcmVwZWwoYm94LnBhZGRpbmcgPSAwLjUsIG1heC5vdmVybGFwcyA9IEluZiwgY29sb3IgPSAiYmxhY2siKSArCiAgI2dlb21fcG9pbnQoKSArIAogIGxhYnMoeCA9ICJGQTEiLCB5ID0gIkZBMiIpICsKICBnZ3RpdGxlKCJDYXB5YmFyYSBBbm5vdGF0aW9uOiBNZWdha2FyeW9jeXRlIFByb2dlbml0b3IgJiBFb3Npbm9waGlsIFByb2dlbml0b3IiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiwKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZC5pdGFsaWMiLCBzaXplID0gMTQpLCAKICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgIHRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZC5pdGFsaWMiLCBzaXplID0gMTQpLAogICAgICAgIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siLCBzaXplID0gMC41KSwKICAgICAgICBheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpKQpgYGAKCklWKSBFb3Npbm9waGlscy5wcm9nZW5pdG9yLU1vbm9jeXRlLnByb2dlbml0b3IgaHlicmlkcwpgYGB7ciwgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9Ny41fQpnZ3Bsb3QobGFiZWxfZGZfbm9fbXVsdGlbd2hpY2gobGFiZWxfZGZfbm9fbXVsdGkkY2VsbC50eXBlICVpbiUgYygiRW9zaW5vcGhpbHMucHJvZ2VuaXRvciIsICJNb25vY3l0ZS5wcm9nZW5pdG9yIikpLF0sIGFlcyh4ID0gVjEsIHkgPSBWMiwgbGFiZWwgPSBjZWxsLnR5cGUsIGNvbG9yID0gY2VsbC50eXBlKSkgKwogIGdlb21fcG9pbnQoZGF0YSA9IG1ldGEuYWxsLCBjb2xvciA9ICJsaWdodGdyZXkiKSArCiAgZ2VvbV9wb2ludChkYXRhID0gbWV0YS5hbGxbd2hpY2gobWV0YS5hbGwkbW9yZV9nYXRoZXJlZF9jdF9uZXcgJWluJSBjKCJFb3Npbm9waGlscy5wcm9nZW5pdG9yIiwgIk1vbm9jeXRlLnByb2dlbml0b3IiKSksXSwgYWVzKGNvbG9yID0gbW9yZV9nYXRoZXJlZF9jdF9uZXcpKSArCiAgZ2VvbV9jaXJjbGUoZGF0YSA9IG1ldGEuYWxsW2NlbGwudGFibGVbd2hpY2goY2VsbC50YWJsZSRpZGVudGl0eSA9PSAiRW9zaW5vcGhpbHMtTW9ub2N5dGUucHJvZ2VuaXRvciIpLCAiY2VsbC5iYyJdLCBdLCBtYXBwaW5nID0gYWVzKHgwID0gVjEsIHkwID0gVjIsIHIgPSAxMDApLCBmaWxsID0gImRhcmtncmV5IiwgaW5oZXJpdC5hZXMgPSBGKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IFJDb2xvckJyZXdlcjo6YnJld2VyLnBhbCgxMiwgIlBhaXJlZCIpW2MoNSw2KV0pICsKICBnZW9tX3RleHRfcmVwZWwoYm94LnBhZGRpbmcgPSAwLjUsIG1heC5vdmVybGFwcyA9IEluZiwgY29sb3IgPSAiYmxhY2siKSArCiAgI2dlb21fcG9pbnQoKSArIAogIGxhYnMoeCA9ICJGQTEiLCB5ID0gIkZBMiIpICsKICBnZ3RpdGxlKCJDYXB5YmFyYSBBbm5vdGF0aW9uOiBFb3Npbm9waGlsIFByb2dlbml0b3IgJiBNb25vY3l0ZSBQcm9nZW5pdG9yIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIsCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQuaXRhbGljIiwgc2l6ZSA9IDE0KSwgCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICB0aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQuaXRhbGljIiwgc2l6ZSA9IDE0KSwKICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiwgc2l6ZSA9IDAuNSksCiAgICAgICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCgo4KSBXZSBuZXh0IHBsb3QgdmlvbGluIHBsb3RzIHRvIGNvbXBhcmUgaHlicmlkIGFuZCBkaXNjcmV0ZSBjZWxsIHBzZXVkb3RpbWUuCgpgYGB7ciwgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9OSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShnZ3B1YnIpCm11bHRpLm1ldGEucHNldWRvdGltZSA8LSBtZXRhLmFsbC5tdWx0aS5jZWxsc1ssYyg1LDE2LDIwKV0KY29sbmFtZXMobXVsdGkubWV0YS5wc2V1ZG90aW1lKVszXSA8LSAibW9yZV9nYXRoZXJlZF9jdF9uZXciCm11bHRpLm1ldGEucHNldWRvdGltZSRjYXRlZ29yeSA8LSAibXVsdGlzIgpyb3duYW1lcyhjZWxsLnRhYmxlLnN1YikgPC0gY2VsbC50YWJsZS5zdWIkY2VsbC5iYwptdWx0aS5tZXRhLnBzZXVkb3RpbWUkbW9yZV9nYXRoZXJlZF9jdF9uZXcgPC0gY2VsbC50YWJsZS5zdWJbcm93bmFtZXMobXVsdGkubWV0YS5wc2V1ZG90aW1lKSwgImlkZW50aXR5Il0KcHNldWRvdGltZS5kdCRjYXRlZ29yeSA8LSAiZW5kcyIKY29tYmluZWQudG8ucGxvdCA8LSByYmluZChtdWx0aS5tZXRhLnBzZXVkb3RpbWUsIHBzZXVkb3RpbWUuZHQpCgpjb21iaW5lZC50by5wbG90JG5ldy5jYXQuMSA8LSBOQQpjb21iaW5lZC50by5wbG90JG5ldy5jYXQuMiA8LSBOQQpjb21iaW5lZC50by5wbG90JG5ldy5jYXQuMyA8LSBOQQpjb21iaW5lZC50by5wbG90JG5ldy5jYXQuNCA8LSBOQQpjb21iaW5lZC50by5wbG90JG5ldy5jYXQuNSA8LSBOQQpjb21iaW5lZC50by5wbG90JG5ldy5jYXQuNiA8LSBOQQoKY29tYmluZWQudG8ucGxvdFt3aGljaChjb21iaW5lZC50by5wbG90JG1vcmVfZ2F0aGVyZWRfY3RfbmV3ICVpbiUgYygiRXJ5dGhyb2JsYXN0IiwgIkVyeXRocm9jeXRlLnByb2dlbml0b3IiLCAiRXJ5dGhyb2JsYXN0LUVyeXRocm9jeXRlLnByb2dlbml0b3IiKSksICJuZXcuY2F0LjEiXSA8LSJFcnl0aHJvYmxhc3QtRXJ5dGhyb2N5dGUucHJvZ2VuaXRvciIKCmNvbWJpbmVkLnRvLnBsb3QkbW9yZV9nYXRoZXJlZF9jdF9uZXdbd2hpY2goY29tYmluZWQudG8ucGxvdCRtb3JlX2dhdGhlcmVkX2N0X25ldz09Ik1vbm9jeXRlLU5ldXRyb3BoaWwiKV0gPC0gIk1vbm9jeXRlLnByb2dlbml0b3ItTmV1dHJvcGhpbCIKY29tYmluZWQudG8ucGxvdFt3aGljaChjb21iaW5lZC50by5wbG90JG1vcmVfZ2F0aGVyZWRfY3RfbmV3ICVpbiUgYygiTW9ub2N5dGUucHJvZ2VuaXRvciIsIk5ldXRyb3BoaWwiLCAiTW9ub2N5dGUucHJvZ2VuaXRvci1OZXV0cm9waGlsIikpLCAibmV3LmNhdC4yIiBdIDwtIk1vbm9jeXRlLnByb2dlbml0b3ItTmV1dHJvcGhpbCIKCmNvbWJpbmVkLnRvLnBsb3Rbd2hpY2goY29tYmluZWQudG8ucGxvdCRtb3JlX2dhdGhlcmVkX2N0X25ldyAlaW4lIGMoIkVyeXRocm9jeXRlLnByb2dlbml0b3ItTWVnYWthcnlvY3l0ZS5wcm9nZW5pdG9yLmNlbGwiLCAiRXJ5dGhyb2N5dGUucHJvZ2VuaXRvciIsIk1lZ2FrYXJ5b2N5dGUucHJvZ2VuaXRvci5jZWxsIikpLCAibmV3LmNhdC40IiBdIDwtIkVyeXRocm9jeXRlLnByb2dlbml0b3ItTWVnYWthcnlvY3l0ZS5wcm9nZW5pdG9yLmNlbGwiCgpjb21iaW5lZC50by5wbG90W3doaWNoKGNvbWJpbmVkLnRvLnBsb3QkbW9yZV9nYXRoZXJlZF9jdF9uZXcgJWluJSBjKCJFb3Npbm9waGlscy1Nb25vY3l0ZS5wcm9nZW5pdG9yIiwgIkVvc2lub3BoaWxzLnByb2dlbml0b3IiLCAiTW9ub2N5dGUucHJvZ2VuaXRvciIpKSwgIm5ldy5jYXQuNSJdIDwtIkVvc2lub3BoaWxzLnByb2dlbml0b3ItTW9ub2N5dGUucHJvZ2VuaXRvciIKCmNvbWJpbmVkLnRvLnBsb3Rbd2hpY2goY29tYmluZWQudG8ucGxvdCRtb3JlX2dhdGhlcmVkX2N0X25ldyAlaW4lIGMoIkVvc2lub3BoaWxzLU1lZ2FrYXJ5b2N5dGUucHJvZ2VuaXRvci5jZWxsIiwgIkVvc2lub3BoaWxzLnByb2dlbml0b3IiLCAiTWVnYWthcnlvY3l0ZS5wcm9nZW5pdG9yLmNlbGwiKSksICJuZXcuY2F0LjYiIF0gPC0iRW9zaW5vcGhpbHMtTWVnYWthcnlvY3l0ZS5wcm9nZW5pdG9yLmNlbGwiCmNzIDwtIHZpcmlkaXMoMjAsIG9wdGlvbiA9ICJBIiwgYmVnaW4gPSAwLjE1LCBlbmQgPSAwLjg1KQoKbXlfY29tcGFyaXNvbnMgPC0gbGlzdCggYygiRXJ5dGhyb2JsYXN0IiwgIkVyeXRocm9ibGFzdC1Fcnl0aHJvY3l0ZS5wcm9nZW5pdG9yIiksIGMoIkVyeXRocm9jeXRlLnByb2dlbml0b3IiLCAiRXJ5dGhyb2JsYXN0LUVyeXRocm9jeXRlLnByb2dlbml0b3IiKSkKZ2dwbG90KGNvbWJpbmVkLnRvLnBsb3RbIWlzLm5hKGNvbWJpbmVkLnRvLnBsb3QkbmV3LmNhdC4xKSwgXSwgYWVzKHggPSBtb3JlX2dhdGhlcmVkX2N0X25ldywgeSA9IGRwdF9wc2V1ZG90aW1lLCBmaWxsID0gY2F0ZWdvcnkpKSArCiAgZ2VvbV92aW9saW4oKSArCiAgZ2VvbV9qaXR0ZXIoY29sb3IgPSAiYmxhY2siLCBzaXplID0gMC44KSArCiAgc2NhbGVfZmlsbF92aXJpZGlzX2Qob3B0aW9uID0gIkEiLCBiZWdpbiA9IDAuMTUsIGVuZCA9IDAuODUpICsKICBzdGF0X3N1bW1hcnkoZnVuLnk9bWVkaWFuLnF1YXJ0aWxlLGdlb209J3BvaW50JywgY29sb3IgPSByZXAoY3NbYygyMCwxLDIwKV0sIGVhY2ggPSAzKSkgKwogIHN0YXRfc3VtbWFyeShmdW4ueT1tZWRpYW4ucXVhcnRpbGUsZ2VvbT0nbGluZScsIGNvbG9yID0gcmVwKGNzW2MoMjAsMSwyMCldLCBlYWNoID0gMykpICsKICBzdGF0X2NvbXBhcmVfbWVhbnMoY29tcGFyaXNvbnMgPSBteV9jb21wYXJpc29ucywgbGFiZWwgPSAiLi5wLnNpZ25pZi4uIikgKwogIGxhYnMoeSA9ICJwc2V1ZG90aW1lIikgKwogIGdndGl0bGUoIkVyeXRocm9ibGFzdC1Fcnl0aHJvY3l0ZS5wcm9nZW5pdG9yIikgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiLAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBmYWNlID0gImJvbGQuaXRhbGljIiksCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiwgZmFjZSA9ICJib2xkLml0YWxpYyIpLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIpKQoKbXlfY29tcGFyaXNvbnMgPC0gbGlzdCggYygiTW9ub2N5dGUucHJvZ2VuaXRvciIsICJNb25vY3l0ZS5wcm9nZW5pdG9yLU5ldXRyb3BoaWwiKSwgYygiTmV1dHJvcGhpbCIsICJNb25vY3l0ZS5wcm9nZW5pdG9yLU5ldXRyb3BoaWwiKSkKZ2dwbG90KGNvbWJpbmVkLnRvLnBsb3RbIWlzLm5hKGNvbWJpbmVkLnRvLnBsb3QkbmV3LmNhdC4yKSwgXSwgYWVzKHggPSBtb3JlX2dhdGhlcmVkX2N0X25ldywgeSA9IGRwdF9wc2V1ZG90aW1lLCBmaWxsID0gY2F0ZWdvcnkpKSArCiAgZ2VvbV92aW9saW4oKSArCiAgZ2VvbV9qaXR0ZXIoY29sb3IgPSAiYmxhY2siLCBzaXplID0gMC44KSArCiAgc2NhbGVfZmlsbF92aXJpZGlzX2Qob3B0aW9uID0gIkEiLCBiZWdpbiA9IDAuMTUsIGVuZCA9IDAuODUpICtzdGF0X3N1bW1hcnkoZnVuLnk9bWVkaWFuLnF1YXJ0aWxlLGdlb209J3BvaW50JywgY29sb3IgPSByZXAoY3NbYygyMCwxLDIwKV0sIGVhY2ggPSAzKSkgKwogIHN0YXRfc3VtbWFyeShmdW4ueT1tZWRpYW4ucXVhcnRpbGUsZ2VvbT0nbGluZScsIGNvbG9yID0gcmVwKGNzW2MoMjAsMSwyMCldLCBlYWNoID0gMykpICsKICBzdGF0X2NvbXBhcmVfbWVhbnMoY29tcGFyaXNvbnMgPSBteV9jb21wYXJpc29ucywgbGFiZWwgPSAiLi5wLnNpZ25pZi4uIikgKwogIGxhYnMoeSA9ICJwc2V1ZG90aW1lIikgKwogIGdndGl0bGUoIk1vbm9jeXRlLnByb2dlbml0b3ItTmV1dHJvcGhpbCIpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiLAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBmYWNlID0gImJvbGQuaXRhbGljIiksCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiwgZmFjZSA9ICJib2xkLml0YWxpYyIpLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIpKQoKbXlfY29tcGFyaXNvbnMgPC0gbGlzdCggYygiTWVnYWthcnlvY3l0ZS5wcm9nZW5pdG9yLmNlbGwiLCAiRXJ5dGhyb2N5dGUucHJvZ2VuaXRvci1NZWdha2FyeW9jeXRlLnByb2dlbml0b3IuY2VsbCIpLCBjKCJFcnl0aHJvY3l0ZS5wcm9nZW5pdG9yIiwgIkVyeXRocm9jeXRlLnByb2dlbml0b3ItTWVnYWthcnlvY3l0ZS5wcm9nZW5pdG9yLmNlbGwiKSkKZ2dwbG90KGNvbWJpbmVkLnRvLnBsb3RbIWlzLm5hKGNvbWJpbmVkLnRvLnBsb3QkbmV3LmNhdC40KSwgXSwgYWVzKHggPSBtb3JlX2dhdGhlcmVkX2N0X25ldywgeSA9IGRwdF9wc2V1ZG90aW1lLCBmaWxsID0gY2F0ZWdvcnkpKSArCiAgZ2VvbV92aW9saW4oKSArCiAgZ2VvbV9qaXR0ZXIoY29sb3IgPSAiYmxhY2siLCBzaXplID0gMC44KSArCiAgc2NhbGVfZmlsbF92aXJpZGlzX2Qob3B0aW9uID0gIkEiLCBiZWdpbiA9IDAuMTUsIGVuZCA9IDAuODUpICsKICBzdGF0X3N1bW1hcnkoZnVuLnk9bWVkaWFuLnF1YXJ0aWxlLGdlb209J3BvaW50JywgY29sb3IgPSByZXAoY3NbYygyMCwxLDIwKV0sIGVhY2ggPSAzKSkgKwogIHN0YXRfc3VtbWFyeShmdW4ueT1tZWRpYW4ucXVhcnRpbGUsZ2VvbT0nbGluZScsIGNvbG9yID0gcmVwKGNzW2MoMjAsMSwyMCldLCBlYWNoID0gMykpICsKICBzdGF0X2NvbXBhcmVfbWVhbnMoY29tcGFyaXNvbnMgPSBteV9jb21wYXJpc29ucywgbGFiZWwgPSAiLi5wLnNpZ25pZi4uIikgKwogIGxhYnMoeSA9ICJwc2V1ZG90aW1lIikgKwogIGdndGl0bGUoIkVyeXRocm9jeXRlLnByb2dlbml0b3ItTWVnYWthcnlvY3l0ZS5wcm9nZW5pdG9yLmNlbGwiKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIsCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGZhY2UgPSAiYm9sZC5pdGFsaWMiKSwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgdGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2LCBmYWNlID0gImJvbGQuaXRhbGljIiksCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIikpCgpteV9jb21wYXJpc29ucyA8LSBsaXN0KCBjKCJNZWdha2FyeW9jeXRlLnByb2dlbml0b3IuY2VsbCIsICJFb3Npbm9waGlscy1NZWdha2FyeW9jeXRlLnByb2dlbml0b3IuY2VsbCIpLCBjKCJFb3Npbm9waGlscy5wcm9nZW5pdG9yIiwgIkVvc2lub3BoaWxzLU1lZ2FrYXJ5b2N5dGUucHJvZ2VuaXRvci5jZWxsIikpCmVvcy5tayA8LSBjb21iaW5lZC50by5wbG90WyFpcy5uYShjb21iaW5lZC50by5wbG90JG5ldy5jYXQuNiksIF0KZW9zLm1rJG1vcmVfZ2F0aGVyZWRfY3RfbmV3IDwtIGZhY3Rvcihlb3MubWskbW9yZV9nYXRoZXJlZF9jdF9uZXcsIGxldmVscyA9IGMoIkVvc2lub3BoaWxzLnByb2dlbml0b3IiLCAiRW9zaW5vcGhpbHMtTWVnYWthcnlvY3l0ZS5wcm9nZW5pdG9yLmNlbGwiLCAiTWVnYWthcnlvY3l0ZS5wcm9nZW5pdG9yLmNlbGwiKSwgb3JkZXJlZCA9IFQpCmdncGxvdChlb3MubWssIGFlcyh4ID0gbW9yZV9nYXRoZXJlZF9jdF9uZXcsIHkgPSBkcHRfcHNldWRvdGltZSwgZmlsbCA9IGNhdGVnb3J5KSkgKwogIGdlb21fdmlvbGluKCkgKwogIGdlb21faml0dGVyKGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDAuOCkgKwogIHNjYWxlX2ZpbGxfdmlyaWRpc19kKG9wdGlvbiA9ICJBIiwgYmVnaW4gPSAwLjE1LCBlbmQgPSAwLjg1KSArCiAgc3RhdF9zdW1tYXJ5KGZ1bi55PW1lZGlhbi5xdWFydGlsZSxnZW9tPSdwb2ludCcsIGNvbG9yID0gcmVwKGNzW2MoMjAsMSwyMCldLCBlYWNoID0gMykpICsKICBzdGF0X3N1bW1hcnkoZnVuLnk9bWVkaWFuLnF1YXJ0aWxlLGdlb209J2xpbmUnLCBjb2xvciA9IHJlcChjc1tjKDIwLDEsMjApXSwgZWFjaCA9IDMpKSArCiAgc3RhdF9jb21wYXJlX21lYW5zKGNvbXBhcmlzb25zID0gbXlfY29tcGFyaXNvbnMsIGxhYmVsID0gIi4ucC5zaWduaWYuLiIpICsKICBsYWJzKHkgPSAicHNldWRvdGltZSIpICsKICBnZ3RpdGxlKCJFb3Npbm9waGlscy1NZWdha2FyeW9jeXRlLnByb2dlbml0b3IuY2VsbCIpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiwKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dCggc2l6ZSA9IDEyKSwKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGZhY2UgPSAiYm9sZC5pdGFsaWMiKSwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgdGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBmYWNlID0gImJvbGQuaXRhbGljIiksCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIikpCgpteV9jb21wYXJpc29ucyA8LSBsaXN0KCBjKCJNb25vY3l0ZS5wcm9nZW5pdG9yIiwgIkVvc2lub3BoaWxzLU1vbm9jeXRlLnByb2dlbml0b3IiKSwgYygiRW9zaW5vcGhpbHMucHJvZ2VuaXRvciIsICJFb3Npbm9waGlscy1Nb25vY3l0ZS5wcm9nZW5pdG9yIikpCmVvcy5tb25vIDwtIGNvbWJpbmVkLnRvLnBsb3RbIWlzLm5hKGNvbWJpbmVkLnRvLnBsb3QkbmV3LmNhdC41KSwgXQplb3MubW9ubyRtb3JlX2dhdGhlcmVkX2N0X25ldyA8LSBmYWN0b3IoZW9zLm1vbm8kbW9yZV9nYXRoZXJlZF9jdF9uZXcsIGxldmVscyA9IGMoIkVvc2lub3BoaWxzLnByb2dlbml0b3IiLCAiRW9zaW5vcGhpbHMtTW9ub2N5dGUucHJvZ2VuaXRvciIsICJNb25vY3l0ZS5wcm9nZW5pdG9yIiksIG9yZGVyZWQgPSBUKQpnZ3Bsb3QoZW9zLm1vbm8sIGFlcyh4ID0gbW9yZV9nYXRoZXJlZF9jdF9uZXcsIHkgPSBkcHRfcHNldWRvdGltZSwgZmlsbCA9IGNhdGVnb3J5KSkgKwogIGdlb21fdmlvbGluKCkgKwogIGdlb21faml0dGVyKGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDAuOCkgKwogIHNjYWxlX2ZpbGxfdmlyaWRpc19kKG9wdGlvbiA9ICJBIiwgYmVnaW4gPSAwLjE1LCBlbmQgPSAwLjg1KSArCiAgc3RhdF9zdW1tYXJ5KGZ1bi55PW1lZGlhbi5xdWFydGlsZSxnZW9tPSdwb2ludCcsIGNvbG9yID0gcmVwKGNzW2MoMjAsMSwyMCldLCBlYWNoID0gMykpICsKICBzdGF0X3N1bW1hcnkoZnVuLnk9bWVkaWFuLnF1YXJ0aWxlLGdlb209J2xpbmUnLCBjb2xvciA9IHJlcChjc1tjKDIwLDEsMjApXSwgZWFjaCA9IDMpKSArCiAgc3RhdF9jb21wYXJlX21lYW5zKGNvbXBhcmlzb25zID0gbXlfY29tcGFyaXNvbnMsIGxhYmVsID0gIi4ucC5zaWduaWYuLiIpICsKICBsYWJzKHkgPSAicHNldWRvdGltZSIpICsKICBnZ3RpdGxlKCJFb3Npbm9waGlscy1Nb25vY3l0ZS5wcm9nZW5pdG9yIikgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiLAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBmYWNlID0gImJvbGQuaXRhbGljIiksCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgZmFjZSA9ICJib2xkLml0YWxpYyIpLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIpKQogIAogIApgYGAKT3ZlcmFsbCwgaHlicmlkIGNlbGxzIG9jY3VweSBpbnRlcm1lZGlhdGUgcHNldWRvdGltZSwgYmV0d2VlbiBkaXNjcmV0ZSBjZWxsIHN0YXRlcy4KCiMjIyBUcmFuc2l0aW9uIHNjb3JlIHZzIENvbm5lY3Rpdml0eSBNYXRyaXggZnJvbSBQQUdBIApXZSBuZXh0IGNvbXBhcmUgY29ubmVjdGl2aXR5IHNjb3JlcyBmcm9tIFBBR0EgdG8gc3VwcG9ydCBvdXIgdHJhbnNpdGlvbiBtZXRyaWMuCgoxKSBXZSBjYWxjdWxhdGUgdGhlIHRyYW5zaXRpb24gc2NvcmVzIGZvciB0aGVzZSBjZWxscwpgYGB7cn0Kc2NvcmVzIDwtIHRyYW5zaXRpb24uc2NvcmUoYWN0dWFsLm11bHRpKQpgYGAKCjIpIFdlIGxvYWQgdGhlIGNvbm5lY3Rpdml0eSBtYXRyaXggZnJvbSBQQUdBCmBgYHtyfQpjb25uZWN0aXZpdHkgPC0gcmVhZC50YWJsZSgifi9EZXNrdG9wL1JlcHJvZHVjaWJpbGl0eS9GaWd1cmUgMi9JbnRlcm1lZGlhdGVzL0RhdGEvY29ubmVjdGl2aXR5X210eC50eHQiKQpyb3duYW1lcyhjb25uZWN0aXZpdHkpIDwtIHJvd25hbWVzKG1ldGEuYWxsKQpjb2xuYW1lcyhjb25uZWN0aXZpdHkpIDwtIHJvd25hbWVzKG1ldGEuYWxsKQpgYGAKCjMpIFdlIGNvbXB1dGUgdGhlIGNvbm5lY3Rpdml0eSBzY29yZSBiYXNlZCBvbiB0aGUgY29ubmVjdGl2aXR5IG1hdHJpeC4gVGhlIHNjb3JlIGlzIGNhbGN1bGF0ZWQgYmFzZWQgb24gdGhlIHdpdGhpbi1jbHVzdGVyIGNlbGwtdG8tY2VsbCBjb25uZWN0aXZpdHkgYW5kIHRoZSBjcm9zcy1jbHVzdGVyIGNlbGwtdG8tY2VsbCBjb25uZWN0aXZpdHkuCmBgYHtyfQppbi5jZWxsLnR5cGUuY29ubmVjdGl2aXR5LnNjb3JlIDwtIGMoKQpvdXQuY2VsbC50eXBlLmNvbm5lY3Rpdml0eS5zY29yZSA8LSBjKCkKZm9yIChpIGluIDE6bnJvdyhzY29yZXMpKSB7CiAgY3Vyci5jdCA8LSByb3duYW1lcyhzY29yZXMpW2ldCiAgY2VsbHMuaW4uY2VsbC50eSA8LSByb3duYW1lcyhtZXRhLmFsbClbd2hpY2gobWV0YS5hbGwkbmV3LmNhbGwgPT0gY3Vyci5jdCldCiAgaWYgKGxlbmd0aChjZWxscy5pbi5jZWxsLnR5KSA+IDApIHsKICAgIGluLmNlbGwudHlwZS5jb25uZWN0aXZpdHkuc2NvcmVbY3Vyci5jdF0gPC0gc3VtKGNvbm5lY3Rpdml0eVtjZWxscy5pbi5jZWxsLnR5LCBjZWxscy5pbi5jZWxsLnR5XSkKICAgIG91dC5jZWxsLnR5cGUuY29ubmVjdGl2aXR5LnNjb3JlW2N1cnIuY3RdIDwtIHN1bShjb25uZWN0aXZpdHlbY2VsbHMuaW4uY2VsbC50eSwgd2hpY2goIWNvbG5hbWVzKGNvbm5lY3Rpdml0eSkgJWluJSBjZWxscy5pbi5jZWxsLnR5KV0pCiAgfQp9Cgppbi5jZWxsLnR5cGUuY29ubmVjdGl2aXR5LnNjb3JlIDwtIGFzLmRhdGEuZnJhbWUoaW4uY2VsbC50eXBlLmNvbm5lY3Rpdml0eS5zY29yZSkKY29sbmFtZXMoaW4uY2VsbC50eXBlLmNvbm5lY3Rpdml0eS5zY29yZSkgPC0gIkluLkNlbGwuVHlwZSIKaW4uY2VsbC50eXBlLmNvbm5lY3Rpdml0eS5zY29yZSRPdXQuQ2VsbC5UeXBlIDwtIG91dC5jZWxsLnR5cGUuY29ubmVjdGl2aXR5LnNjb3JlW3Jvd25hbWVzKGluLmNlbGwudHlwZS5jb25uZWN0aXZpdHkuc2NvcmUpXQppbi5jZWxsLnR5cGUuY29ubmVjdGl2aXR5LnNjb3JlJHRyYW5zaXRpb24uc2NvcmUgPC0gc2NvcmVzW3Jvd25hbWVzKGluLmNlbGwudHlwZS5jb25uZWN0aXZpdHkuc2NvcmUpLCAiZW50cm9weSJdCmBgYAoKNCkgUGxvdCB0byBhc3Nlc3MgY29ycmVsYXRpb24KYGBge3J9CmdncGxvdChpbi5jZWxsLnR5cGUuY29ubmVjdGl2aXR5LnNjb3JlLCBhZXMoeCA9IGxvZzFwKHRyYW5zaXRpb24uc2NvcmUpLCB5ID0gbG9nMXAoT3V0LkNlbGwuVHlwZSkpKSArCiAgZ2VvbV9wb2ludCgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiLAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgZmFjZSA9ICJib2xkLml0YWxpYyIpLAogICAgICAgIHRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgZmFjZSA9ICJib2xkLml0YWxpYyIpLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gJ2dyZXknLCBsaW5ldHlwZSA9ICdkYXNoZWQnKSwgCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIikpCmBgYAoKNSkgQ2FsY3VsYXRlIFBlYXJzb24ncyBDb3JyZWxhdGlvbgpgYGB7cn0KY29yKGluLmNlbGwudHlwZS5jb25uZWN0aXZpdHkuc2NvcmUkT3V0LkNlbGwuVHlwZSwgaW4uY2VsbC50eXBlLmNvbm5lY3Rpdml0eS5zY29yZSR0cmFuc2l0aW9uLnNjb3JlKQpgYGAKCgo=